Adjust property editors

This commit is contained in:
Alexander Nozik 2021-08-07 11:27:57 +03:00
parent 787c841a51
commit 8d21d3cd74
101 changed files with 1410 additions and 1246 deletions

View File

@ -1,18 +1,13 @@
plugins { plugins {
id("ru.mipt.npm.gradle.project") id("ru.mipt.npm.gradle.project")
//Override kotlin version
// val kotlinVersion = "1.5.20-RC"
// kotlin("multiplatform") version(kotlinVersion) apply false
// kotlin("jvm") version(kotlinVersion) apply false
// kotlin("js") version(kotlinVersion) apply false
} }
val dataforgeVersion by extra("0.5.0-dev-2") val dataforgeVersion by extra("0.5.0-dev-9")
val fxVersion by extra("11") val fxVersion by extra("11")
allprojects { allprojects {
repositories { repositories {
mavenLocal()
mavenCentral() mavenCentral()
jcenter() jcenter()
maven("https://repo.kotlin.link") maven("https://repo.kotlin.link")
@ -29,7 +24,7 @@ subprojects {
} }
} }
ksciencePublish{ ksciencePublish {
github("visionforge") github("visionforge")
space() space()
sonatype() sonatype()

View File

@ -1,7 +1,7 @@
package space.kscience.visionforge.gdml package space.kscience.visionforge.gdml
import space.kscience.dataforge.meta.string import space.kscience.dataforge.meta.string
import space.kscience.dataforge.names.toName import space.kscience.dataforge.names.Name
import space.kscience.dataforge.values.asValue import space.kscience.dataforge.values.asValue
import space.kscience.gdml.GdmlShowCase import space.kscience.gdml.GdmlShowCase
import space.kscience.visionforge.setProperty import space.kscience.visionforge.setProperty
@ -24,7 +24,7 @@ class GDMLVisionTest {
@Test @Test
fun testPrototypeProperty() { fun testPrototypeProperty() {
val vision = GdmlShowCase.cubes().toVision() val vision = GdmlShowCase.cubes().toVision()
val child = vision["composite-000.segment-0".toName()] val child = vision[Name.of("composite-000","segment-0")]
assertNotNull(child) assertNotNull(child)
child.setProperty(SolidMaterial.MATERIAL_COLOR_KEY, "red".asValue()) child.setProperty(SolidMaterial.MATERIAL_COLOR_KEY, "red".asValue())
assertEquals("red", child.getProperty(SolidMaterial.MATERIAL_COLOR_KEY).string) assertEquals("red", child.getProperty(SolidMaterial.MATERIAL_COLOR_KEY).string)

View File

@ -8,8 +8,8 @@ import space.kscience.dataforge.context.fetch
import space.kscience.gdml.GdmlShowCase import space.kscience.gdml.GdmlShowCase
import space.kscience.visionforge.VisionManager import space.kscience.visionforge.VisionManager
import space.kscience.visionforge.describedProperties import space.kscience.visionforge.describedProperties
import space.kscience.visionforge.editor.VisualObjectEditorFragment import space.kscience.visionforge.editor.VisionEditorFragment
import space.kscience.visionforge.editor.VisualObjectTreeFragment import space.kscience.visionforge.editor.VisionTreeFragment
import space.kscience.visionforge.gdml.toVision import space.kscience.visionforge.gdml.toVision
import space.kscience.visionforge.solid.FX3DPlugin import space.kscience.visionforge.solid.FX3DPlugin
import space.kscience.visionforge.solid.FXCanvas3D import space.kscience.visionforge.solid.FXCanvas3D
@ -29,25 +29,24 @@ class GDMLView : View() {
private val visionManager = context.fetch(VisionManager) private val visionManager = context.fetch(VisionManager)
private val canvas = FXCanvas3D(fx3d) private val canvas = FXCanvas3D(fx3d)
private val treeFragment = VisualObjectTreeFragment().apply { private val treeFragment = VisionTreeFragment().apply {
this.itemProperty.bind(canvas.rootObjectProperty) this.itemProperty.bind(canvas.rootObjectProperty)
} }
private val propertyEditor = VisualObjectEditorFragment { private val propertyEditor = VisionEditorFragment {
it.describedProperties it.describedProperties
}.apply { }.apply {
descriptorProperty.set(SolidMaterial.descriptor) descriptorProperty.set(SolidMaterial.descriptor)
itemProperty.bind(treeFragment.selectedProperty) itemProperty.bind(treeFragment.selectedProperty)
} }
override val root: Parent = borderpane { override val root: Parent = borderpane {
top { top {
buttonbar { buttonbar {
button("Load GDML/json") { button("Load GDML/json") {
action { action {
val file = chooseFile("Select a GDML/json file", filters = fileNameFilter).firstOrNull() val file = chooseFile("Select a GDML/json file", filters = fileNameFilter).firstOrNull()
if(file!= null) { if (file != null) {
runAsync { runAsync {
visionManager.readFile(file) as Solid visionManager.readFile(file) as Solid
} ui { } ui {

View File

@ -6,6 +6,7 @@ import ru.mipt.npm.muon.monitor.Monitor.UPPER_LAYER_Z
import space.kscience.visionforge.VisionManager import space.kscience.visionforge.VisionManager
import space.kscience.visionforge.removeAll import space.kscience.visionforge.removeAll
import space.kscience.visionforge.root import space.kscience.visionforge.root
import space.kscience.visionforge.setProperty
import space.kscience.visionforge.solid.* import space.kscience.visionforge.solid.*
import kotlin.math.PI import kotlin.math.PI

View File

@ -67,6 +67,7 @@ kotlin {
val jvmMain by getting{ val jvmMain by getting{
dependencies { dependencies {
api(project(":visionforge-server")) api(project(":visionforge-server"))
api(project(":visionforge-markdown"))
api("ch.qos.logback:logback-classic:1.2.3") api("ch.qos.logback:logback-classic:1.2.3")
implementation("com.github.Ricky12Awesome:json-schema-serialization:0.6.6") implementation("com.github.Ricky12Awesome:json-schema-serialization:0.6.6")
} }

View File

@ -0,0 +1,91 @@
package space.kscience.visionforge.examples
import kotlinx.html.div
import kotlinx.html.h1
import space.kscience.dataforge.context.Context
import space.kscience.plotly.layout
import space.kscience.plotly.models.ScatterMode
import space.kscience.plotly.models.TextPosition
import space.kscience.plotly.scatter
import space.kscience.visionforge.html.ResourceLocation
import space.kscience.visionforge.markup.markdown
import space.kscience.visionforge.plotly.PlotlyPlugin
import space.kscience.visionforge.plotly.plotly
import space.kscience.visionforge.solid.*
fun main() {
val context = Context {
plugin(Solids)
plugin(PlotlyPlugin)
}
context.makeVisionFile(resourceLocation = ResourceLocation.SYSTEM) {
markdown {
//language=markdown
"""
# Section
**TBD**
## Subsection
""".trimIndent()
}
div {
h1 { +"Canvas" }
vision("canvas") {
solid {
box(100, 100, 100)
material {
emissiveColor("red")
}
}
}
}
vision("plot") {
plotly {
scatter {
x(1, 2, 3, 4)
y(10, 15, 13, 17)
mode = ScatterMode.markers
name = "Team A"
text("A-1", "A-2", "A-3", "A-4", "A-5")
textposition = TextPosition.`top center`
textfont {
family = "Raleway, sans-serif"
}
marker { size = 12 }
}
scatter {
x(2, 3, 4, 5)
y(10, 15, 13, 17)
mode = ScatterMode.lines
name = "Team B"
text("B-a", "B-b", "B-c", "B-d", "B-e")
textposition = TextPosition.`bottom center`
textfont {
family = "Times New Roman"
}
marker { size = 12 }
}
layout {
title = "Data Labels Hover"
xaxis {
range(0.75..5.25)
}
legend {
y = 0.5
font {
family = "Arial, sans-serif"
size = 20
color("grey")
}
}
}
}
}
}
}

View File

@ -26,7 +26,7 @@ public fun Context.makeVisionFile(
content: VisionTagConsumer<*>.() -> Unit content: VisionTagConsumer<*>.() -> Unit
): Unit { ): Unit {
val actualPath = page(title, content = content).makeFile(path) { actualPath -> val actualPath = page(title, content = content).makeFile(path) { actualPath ->
mapOf("threeJs" to scriptHeader("js/visionforge-playground.js", resourceLocation, actualPath)) mapOf("playground" to scriptHeader("js/visionforge-playground.js", resourceLocation, actualPath))
} }
if (show) Desktop.getDesktop().browse(actualPath.toFile().toURI()) if (show) Desktop.getDesktop().browse(actualPath.toFile().toURI())
} }

View File

@ -2,9 +2,7 @@ package space.kscience.visionforge.examples
import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Context
import space.kscience.visionforge.html.ResourceLocation import space.kscience.visionforge.html.ResourceLocation
import space.kscience.visionforge.solid.Solids import space.kscience.visionforge.solid.*
import space.kscience.visionforge.solid.box
import space.kscience.visionforge.solid.solid
fun main() { fun main() {
val context = Context { val context = Context {
@ -15,6 +13,9 @@ fun main() {
vision("canvas") { vision("canvas") {
solid { solid {
box(100, 100, 100) box(100, 100, 100)
material {
emissiveColor("red")
}
} }
} }
} }

View File

@ -8,7 +8,7 @@ import kotlinx.coroutines.launch
import kotlinx.html.div import kotlinx.html.div
import kotlinx.html.h1 import kotlinx.html.h1
import space.kscience.dataforge.context.Global import space.kscience.dataforge.context.Global
import space.kscience.dataforge.names.toName import space.kscience.dataforge.names.Name
import space.kscience.visionforge.solid.* import space.kscience.visionforge.solid.*
import space.kscience.visionforge.three.server.* import space.kscience.visionforge.three.server.*
import space.kscience.visionforge.visionManager import space.kscience.visionforge.visionManager
@ -42,7 +42,7 @@ fun main() {
val randomLayer = Random.nextInt(1, 11) val randomLayer = Random.nextInt(1, 11)
val randomI = Random.nextInt(1, 4) val randomI = Random.nextInt(1, 4)
val randomJ = Random.nextInt(1, 4) val randomJ = Random.nextInt(1, 4)
val target = "layer[$randomLayer].segment[$randomI,$randomJ]".toName() val target = Name.parse("layer[$randomLayer].segment[$randomI,$randomJ]")
val targetVision = sat[target] as Solid val targetVision = sat[target] as Solid
targetVision.color("red") targetVision.color("red")
delay(1000) delay(1000)

View File

@ -1,7 +1,8 @@
package space.kscience.visionforge package space.kscience.visionforge.solid.demo
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.visionforge.Vision
public interface VisionLayout<in V: Vision> { public interface VisionLayout<in V: Vision> {
public fun render(name: Name, vision: V, meta: Meta = Meta.EMPTY) public fun render(name: Name, vision: V, meta: Meta = Meta.EMPTY)

View File

@ -6,9 +6,8 @@ import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.invoke import space.kscience.dataforge.meta.invoke
import space.kscience.dataforge.names.toName import space.kscience.dataforge.names.Name
import space.kscience.visionforge.Colors import space.kscience.visionforge.Colors
import space.kscience.visionforge.VisionLayout
import space.kscience.visionforge.solid.* import space.kscience.visionforge.solid.*
import space.kscience.visionforge.solid.specifications.Canvas3DOptions import space.kscience.visionforge.solid.specifications.Canvas3DOptions
import space.kscience.visionforge.visible import space.kscience.visionforge.visible
@ -23,7 +22,7 @@ fun VisionLayout<Solid>.demo(name: String, title: String = name, block: SolidGro
"title" put title "title" put title
} }
val vision = SolidGroup(block) val vision = SolidGroup(block)
render(name.toName(), vision) render(Name.parse(name), vision)
} }
val canvasOptions = Canvas3DOptions { val canvasOptions = Canvas3DOptions {

View File

@ -15,7 +15,6 @@ import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.get import space.kscience.dataforge.meta.get
import space.kscience.dataforge.meta.string import space.kscience.dataforge.meta.string
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.visionforge.VisionLayout
import space.kscience.visionforge.solid.Solid import space.kscience.visionforge.solid.Solid
import space.kscience.visionforge.solid.three.ThreeCanvas import space.kscience.visionforge.solid.three.ThreeCanvas
import space.kscience.visionforge.solid.three.ThreePlugin import space.kscience.visionforge.solid.three.ThreePlugin

View File

@ -7,7 +7,6 @@ import space.kscience.dataforge.context.Global
import space.kscience.dataforge.context.fetch import space.kscience.dataforge.context.fetch
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.visionforge.VisionLayout
import space.kscience.visionforge.solid.FX3DPlugin import space.kscience.visionforge.solid.FX3DPlugin
import space.kscience.visionforge.solid.FXCanvas3D import space.kscience.visionforge.solid.FXCanvas3D
import space.kscience.visionforge.solid.Solid import space.kscience.visionforge.solid.Solid

View File

@ -2,12 +2,14 @@ package space.kscience.visionforge.demo
import javafx.geometry.Orientation import javafx.geometry.Orientation
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.asConfig import space.kscience.dataforge.meta.MutableMeta
import space.kscience.dataforge.meta.descriptors.NodeDescriptor import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.meta.descriptors.node
import space.kscience.dataforge.meta.descriptors.value
import space.kscience.dataforge.values.ValueType import space.kscience.dataforge.values.ValueType
import space.kscience.visionforge.editor.ConfigEditor import space.kscience.visionforge.editor.FXMetaModel
import space.kscience.visionforge.editor.FXMeta
import space.kscience.visionforge.editor.MetaViewer import space.kscience.visionforge.editor.MetaViewer
import space.kscience.visionforge.editor.MutableMetaEditor
import tornadofx.* import tornadofx.*
@ -15,7 +17,7 @@ class MetaEditorDemoApp : App(MetaEditorDemo::class)
class MetaEditorDemo : View("Meta editor demo") { class MetaEditorDemo : View("Meta editor demo") {
val meta = Meta { val meta = MutableMeta {
"aNode" put { "aNode" put {
"innerNode" put { "innerNode" put {
"innerValue" put true "innerValue" put true
@ -23,18 +25,16 @@ class MetaEditorDemo : View("Meta editor demo") {
"b" put 223 "b" put 223
"c" put "StringValue" "c" put "StringValue"
} }
}.asConfig() }
val descriptor = NodeDescriptor { val descriptor = MetaDescriptor {
node("aNode") { node("aNode") {
info = "A root demo node" info = "A root demo node"
value("b") { value("b", ValueType.NUMBER) {
info = "b number value" info = "b number value"
type(ValueType.NUMBER)
} }
node("otherNode") { node("otherNode") {
value("otherValue") { value("otherValue", ValueType.BOOLEAN) {
type(ValueType.BOOLEAN)
default(false) default(false)
info = "default value" info = "default value"
} }
@ -46,12 +46,13 @@ class MetaEditorDemo : View("Meta editor demo") {
} }
} }
private val rootNode = FXMeta.root(meta, descriptor) private val rootNode = FXMetaModel.root(meta, descriptor)
override val root = override val root = splitpane(
splitpane(Orientation.HORIZONTAL, MetaViewer(rootNode).root, ConfigEditor( Orientation.HORIZONTAL,
rootNode MetaViewer(rootNode as Meta).root,
).root) MutableMetaEditor(rootNode as FXMetaModel<MutableMeta>).root
)
} }
fun main() { fun main() {

View File

@ -1,5 +1,5 @@
## Library design ## Library design
The central point of the library design is the `Vision` interface. The `Vision` stores an optional reference to its parent and is able to store a number of mutable or read-only properties. Each property is represented by its `Name`, and a `MetaItem` value-tree, both following DataForge library specification (discussed in the [Appendix](appendix.md)). The `Vision` objects are organized in a tree using `VisionGroup` as nodes. `VisionGroup` additionally to all `Vision` properties holds a `children` container that holds named references to its direct children `Vision`s. Thus, `Vision`s form a doubly linked tree (a parent stores references to all its children and children store a reference to the parent). The central point of the library design is the `Vision` interface. The `Vision` stores an optional reference to its parent and is able to store a number of mutable or read-only properties. Each property is represented by its `Name`, and a `Meta` value-tree, both following DataForge library specification (discussed in the [Appendix](appendix.md)). The `Vision` objects are organized in a tree using `VisionGroup` as nodes. `VisionGroup` additionally to all `Vision` properties holds a `children` container that holds named references to its direct children `Vision`s. Thus, `Vision`s form a doubly linked tree (a parent stores references to all its children and children store a reference to the parent).
An important concept using in the VisionForge is the property layering mechanism. It means that if the property with a given name is not found in the `Vision` it is requested from, it could be requested from the parent `Vision`, form the style declaration, the prototype for the vision or any other place defined by the component author. For example, let's take a `color` attribute used in 3D visualization. When one draws a group of objects, he usually wants to make the color of all objects in the group to be defined by a single handle in the group common ancestor. So when the parent color changes, all children color must follow suite, but we also want to change children color individually without changing the parent. In this case two property layers are defined: An important concept using in the VisionForge is the property layering mechanism. It means that if the property with a given name is not found in the `Vision` it is requested from, it could be requested from the parent `Vision`, form the style declaration, the prototype for the vision or any other place defined by the component author. For example, let's take a `color` attribute used in 3D visualization. When one draws a group of objects, he usually wants to make the color of all objects in the group to be defined by a single handle in the group common ancestor. So when the parent color changes, all children color must follow suite, but we also want to change children color individually without changing the parent. In this case two property layers are defined:

View File

@ -3,7 +3,7 @@
interface Vision{ interface Vision{
val parent: Vision? val parent: Vision?
fun getProperty(name): MetaItem? fun getProperty(name): Meta?
fun setProperty(name, value) fun setProperty(name, value)
} }

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

View File

@ -1,6 +1,6 @@
pluginManagement { pluginManagement {
val toolsVersion = "0.10.0" val toolsVersion = "0.10.2"
repositories { repositories {
maven("https://repo.kotlin.link") maven("https://repo.kotlin.link")

View File

@ -11,5 +11,5 @@ dependencies {
implementation(npm("file-saver", "2.0.2")) implementation(npm("file-saver", "2.0.2"))
implementation(npm("bootstrap","4.6.0")) implementation(npm("bootstrap","4.6.0"))
implementation(npm("jquery","3.5.1")) implementation(npm("jquery","3.5.1"))
implementation(npm("popper.js","1.16.1")) implementation(npm("@popperjs/core","2.9.3"))
} }

View File

@ -12,6 +12,7 @@ import org.w3c.files.BlobPropertyBag
import react.* import react.*
import react.dom.attrs import react.dom.attrs
import react.dom.button import react.dom.button
import space.kscience.dataforge.meta.descriptors.defaultNode
import space.kscience.dataforge.meta.withDefault import space.kscience.dataforge.meta.withDefault
import space.kscience.visionforge.Vision import space.kscience.visionforge.Vision
import space.kscience.visionforge.react.flexColumn import space.kscience.visionforge.react.flexColumn
@ -43,7 +44,7 @@ public external interface CanvasControlsProps : RProps {
public var vision: Vision? public var vision: Vision?
} }
public val CanvasControls: FunctionalComponent<CanvasControlsProps> = functionalComponent("CanvasControls") { props -> public val CanvasControls: FunctionComponent<CanvasControlsProps> = functionalComponent("CanvasControls") { props ->
flexColumn { flexColumn {
flexRow { flexRow {
css { css {
@ -66,7 +67,7 @@ public val CanvasControls: FunctionalComponent<CanvasControlsProps> = functional
} }
propertyEditor( propertyEditor(
ownProperties = props.canvasOptions, ownProperties = props.canvasOptions,
allProperties = props.canvasOptions.withDefault(Canvas3DOptions.descriptor.defaultMeta), allProperties = props.canvasOptions.meta.withDefault(Canvas3DOptions.descriptor.defaultNode),
descriptor = Canvas3DOptions.descriptor, descriptor = Canvas3DOptions.descriptor,
expanded = false expanded = false
) )

View File

@ -18,7 +18,7 @@ public external class TabProps : RProps {
} }
@JsExport @JsExport
public val Tab: FunctionalComponent<TabProps> = functionalComponent { props -> public val Tab: FunctionComponent<TabProps> = functionalComponent { props ->
props.children() props.children()
} }
@ -27,7 +27,7 @@ public external class TabPaneProps : RProps {
} }
@JsExport @JsExport
public val TabPane: FunctionalComponent<TabPaneProps> = functionalComponent("TabPane") { props -> public val TabPane: FunctionComponent<TabPaneProps> = functionalComponent("TabPane") { props ->
var activeTab: String? by useState(props.activeTab) var activeTab: String? by useState(props.activeTab)
val children: Array<out ReactElement?> = Children.map(props.children) { val children: Array<out ReactElement?> = Children.map(props.children) {

View File

@ -3,22 +3,25 @@ package space.kscience.visionforge.bootstrap
import org.w3c.dom.Element import org.w3c.dom.Element
import react.RBuilder import react.RBuilder
import react.dom.render import react.dom.render
import space.kscience.dataforge.meta.descriptors.NodeDescriptor import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.visionforge.* import space.kscience.visionforge.Vision
import space.kscience.visionforge.getStyle
import space.kscience.visionforge.meta
import space.kscience.visionforge.react.metaViewer import space.kscience.visionforge.react.metaViewer
import space.kscience.visionforge.react.propertyEditor import space.kscience.visionforge.react.propertyEditor
import space.kscience.visionforge.solid.SolidReference import space.kscience.visionforge.solid.SolidReference
import space.kscience.visionforge.styles
public fun RBuilder.visionPropertyEditor( public fun RBuilder.visionPropertyEditor(
vision: Vision, vision: Vision,
descriptor: NodeDescriptor? = vision.descriptor, descriptor: MetaDescriptor? = vision.descriptor,
key: Any? = null, key: Any? = null,
) { ) {
card("Properties") { card("Properties") {
propertyEditor( propertyEditor(
ownProperties = vision.ownProperties, ownProperties = vision.meta(false,false,false),
allProperties = vision.allProperties(), allProperties = vision.meta(),
updateFlow = vision.propertyChanges, updateFlow = vision.propertyChanges,
descriptor = descriptor, descriptor = descriptor,
key = key key = key
@ -47,7 +50,7 @@ public fun RBuilder.visionPropertyEditor(
public fun Element.visionPropertyEditor( public fun Element.visionPropertyEditor(
item: Vision, item: Vision,
descriptor: NodeDescriptor? = item.descriptor, descriptor: MetaDescriptor? = item.descriptor,
): Unit = render(this) { ): Unit = render(this) {
visionPropertyEditor(item, descriptor = descriptor) visionPropertyEditor(item, descriptor = descriptor)
} }

View File

@ -8,12 +8,11 @@ import react.*
import react.dom.a import react.dom.a
import react.dom.attrs import react.dom.attrs
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.MetaItemNode import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.meta.MetaItemValue import space.kscience.dataforge.meta.descriptors.defaultNode
import space.kscience.dataforge.meta.descriptors.ItemDescriptor
import space.kscience.dataforge.meta.descriptors.NodeDescriptor
import space.kscience.dataforge.meta.descriptors.get import space.kscience.dataforge.meta.descriptors.get
import space.kscience.dataforge.meta.get import space.kscience.dataforge.meta.get
import space.kscience.dataforge.meta.isLeaf
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.NameToken import space.kscience.dataforge.names.NameToken
import space.kscience.dataforge.names.lastOrNull import space.kscience.dataforge.names.lastOrNull
@ -41,18 +40,19 @@ public external interface MetaViewerProps : RProps {
/** /**
* Root descriptor * Root descriptor
*/ */
public var descriptor: NodeDescriptor? public var descriptor: MetaDescriptor?
} }
private val MetaViewerItem: FunctionalComponent<MetaViewerProps> = functionalComponent("MetaViewerItem") { props -> private val MetaViewerItem: FunctionComponent<MetaViewerProps> = functionalComponent("MetaViewerItem") { props ->
metaViewerItem(props) metaViewerItem(props)
} }
private fun RBuilder.metaViewerItem(props: MetaViewerProps) { private fun RBuilder.metaViewerItem(props: MetaViewerProps) {
var expanded: Boolean by useState { true } var expanded: Boolean by useState { true }
val item = props.root[props.name] val item = props.root[props.name]
val descriptorItem: ItemDescriptor? = props.descriptor?.get(props.name) val descriptorItem: MetaDescriptor? = props.descriptor?.get(props.name)
val actualItem = item ?: descriptorItem?.defaultValue val actualValue = item?.value ?: descriptorItem?.defaultValue
val actualMeta = item ?: descriptorItem?.defaultNode
val token = props.name.lastOrNull()?.toString() ?: props.rootName ?: "" val token = props.name.lastOrNull()?.toString() ?: props.rootName ?: ""
@ -60,90 +60,75 @@ private fun RBuilder.metaViewerItem(props: MetaViewerProps) {
expanded = !expanded expanded = !expanded
} }
when (actualItem) { flexRow {
is MetaItemNode -> { css {
flexRow { alignItems = Align.center
}
if (actualMeta?.isLeaf == false) {
styledSpan {
css { css {
alignItems = Align.center +TreeStyles.treeCaret
} if (expanded) {
styledSpan { +TreeStyles.treeCaredDown
css {
+TreeStyles.treeCaret
if (expanded) {
+TreeStyles.treeCaredDown
}
}
attrs {
onClickFunction = expanderClick
} }
} }
styledSpan { attrs {
css { onClickFunction = expanderClick
+TreeStyles.treeLabel
if (item == null) {
+TreeStyles.treeLabelInactive
}
}
+token
}
}
if (expanded) {
flexColumn {
css {
+TreeStyles.tree
}
val keys = buildSet {
(descriptorItem as? NodeDescriptor)?.items?.keys?.forEach {
add(NameToken(it))
}
actualItem.node.items.keys.let { addAll(it) }
}
keys.filter { !it.body.startsWith("@") }.forEach { token ->
styledDiv {
css {
+TreeStyles.treeItem
}
child(MetaViewerItem) {
attrs {
this.key = props.name.toString()
this.root = props.root
this.name = props.name + token
this.descriptor = props.descriptor
}
}
//configEditor(props.root, props.name + token, props.descriptor, props.default)
}
}
} }
} }
} }
is MetaItemValue -> {
flexRow { styledSpan {
css { css {
alignItems = Align.center +TreeStyles.treeLabel
if (item == null) {
+TreeStyles.treeLabelInactive
} }
styledSpan { }
+token
}
styledDiv {
a {
+actualValue.toString()
}
}
}
if (expanded) {
flexColumn {
css {
+TreeStyles.tree
}
val keys = buildSet {
descriptorItem?.children?.keys?.forEach {
add(NameToken(it))
}
actualMeta!!.items.keys.let { addAll(it) }
}
keys.filter { !it.body.startsWith("@") }.forEach { token ->
styledDiv {
css { css {
+TreeStyles.treeLabel +TreeStyles.treeItem
if (item == null) { }
+TreeStyles.treeLabelInactive child(MetaViewerItem) {
attrs {
this.key = props.name.toString()
this.root = props.root
this.name = props.name + token
this.descriptor = props.descriptor
} }
} }
+token //configEditor(props.root, props.name + token, props.descriptor, props.default)
}
styledDiv {
a {
+actualItem.value.toString()
}
} }
} }
} }
} }
} }
@JsExport @JsExport
public val MetaViewer: FunctionalComponent<MetaViewerProps> = public val MetaViewer: FunctionComponent<MetaViewerProps> =
functionalComponent<MetaViewerProps>("MetaViewer") { props -> functionalComponent<MetaViewerProps>("MetaViewer") { props ->
child(MetaViewerItem) { child(MetaViewerItem) {
attrs { attrs {
@ -155,7 +140,7 @@ public val MetaViewer: FunctionalComponent<MetaViewerProps> =
} }
} }
public fun RBuilder.metaViewer(meta: Meta, descriptor: NodeDescriptor? = null, key: Any? = null) { public fun RBuilder.metaViewer(meta: Meta, descriptor: MetaDescriptor? = null, key: Any? = null) {
child(MetaViewer) { child(MetaViewer) {
attrs { attrs {
this.key = key?.toString() ?: "" this.key = key?.toString() ?: ""

View File

@ -5,23 +5,23 @@ import org.w3c.dom.HTMLOptionElement
import org.w3c.dom.HTMLSelectElement import org.w3c.dom.HTMLSelectElement
import org.w3c.dom.asList import org.w3c.dom.asList
import org.w3c.dom.events.Event import org.w3c.dom.events.Event
import react.FunctionalComponent import react.FunctionComponent
import react.dom.attrs import react.dom.attrs
import react.dom.option import react.dom.option
import react.dom.select import react.dom.select
import react.functionalComponent import react.functionalComponent
import react.useState import react.useState
import space.kscience.dataforge.meta.value import space.kscience.dataforge.meta.descriptors.allowedValues
import space.kscience.dataforge.values.asValue import space.kscience.dataforge.values.asValue
import space.kscience.dataforge.values.string import space.kscience.dataforge.values.string
@JsExport @JsExport
public val MultiSelectChooser: FunctionalComponent<ValueChooserProps> = public val MultiSelectChooser: FunctionComponent<ValueChooserProps> =
functionalComponent("MultiSelectChooser") { props -> functionalComponent("MultiSelectChooser") { props ->
var selectedItems by useState { props.item.value?.list ?: emptyList() } var selectedItems by useState { props.item?.value?.list ?: emptyList() }
val onChange: (Event) -> Unit = { event: Event -> val onChange: (Event) -> Unit = { event: Event ->
val newSelected= (event.target as HTMLSelectElement).selectedOptions.asList() val newSelected = (event.target as HTMLSelectElement).selectedOptions.asList()
.map { (it as HTMLOptionElement).value.asValue() } .map { (it as HTMLOptionElement).value.asValue() }
props.valueChanged?.invoke(newSelected.asValue()) props.valueChanged?.invoke(newSelected.asValue())
selectedItems = newSelected selectedItems = newSelected

View File

@ -18,14 +18,10 @@ import react.*
import react.dom.attrs import react.dom.attrs
import react.dom.render import react.dom.render
import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.*
import space.kscience.dataforge.meta.descriptors.ItemDescriptor import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.meta.descriptors.NodeDescriptor import space.kscience.dataforge.meta.descriptors.ValueRequirement
import space.kscience.dataforge.meta.descriptors.ValueDescriptor
import space.kscience.dataforge.meta.descriptors.get import space.kscience.dataforge.meta.descriptors.get
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.*
import space.kscience.dataforge.names.NameToken
import space.kscience.dataforge.names.lastOrNull
import space.kscience.dataforge.names.plus
import space.kscience.dataforge.values.Value import space.kscience.dataforge.values.Value
import space.kscience.visionforge.hidden import space.kscience.visionforge.hidden
import styled.css import styled.css
@ -38,12 +34,12 @@ public external interface PropertyEditorProps : RProps {
/** /**
* Root config object - always non null * Root config object - always non null
*/ */
public var ownProperties: MutableItemProvider public var ownProperties: MutableMetaProvider
/** /**
* Provide default item (greyed out if used) * Provide default item (greyed out if used)
*/ */
public var allProperties: ItemProvider? public var allProperties: MetaProvider?
/** /**
* Full path to the displayed node in [ownProperties]. Could be empty * Full path to the displayed node in [ownProperties]. Could be empty
@ -53,7 +49,7 @@ public external interface PropertyEditorProps : RProps {
/** /**
* Root descriptor * Root descriptor
*/ */
public var descriptor: NodeDescriptor? public var descriptor: MetaDescriptor?
/** /**
* A coroutine scope for updates * A coroutine scope for updates
@ -71,21 +67,21 @@ public external interface PropertyEditorProps : RProps {
public var expanded: Boolean? public var expanded: Boolean?
} }
private val PropertyEditorItem: FunctionalComponent<PropertyEditorProps> = private val PropertyEditorItem: FunctionComponent<PropertyEditorProps> =
functionalComponent("ConfigEditorItem") { props -> functionalComponent("ConfigEditorItem") { props ->
propertyEditorItem(props) propertyEditorItem(props)
} }
private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) { private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) {
var expanded: Boolean by useState { props.expanded ?: true } var expanded: Boolean by useState { props.expanded ?: true }
val descriptorItem: ItemDescriptor? = props.descriptor?.get(props.name) val descriptor: MetaDescriptor? = props.descriptor?.get(props.name)
var ownProperty: MetaItem? by useState { props.ownProperties.getItem(props.name) } var ownProperty: Meta? by useState { props.ownProperties.getMeta(props.name) }
val actualItem: MetaItem? = props.allProperties?.getItem(props.name) val actualMeta = props.allProperties?.getMeta(props.name)
val token = props.name.lastOrNull()?.toString() ?: "Properties" val token = props.name.lastOrNull()?.toString() ?: "Properties"
fun update() { fun update() {
ownProperty = props.ownProperties.getItem(props.name) ownProperty = props.ownProperties.getMeta(props.name)
} }
if (props.updateFlow != null) { if (props.updateFlow != null) {
@ -109,7 +105,7 @@ private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) {
if (it == null) { if (it == null) {
props.ownProperties.remove(props.name) props.ownProperties.remove(props.name)
} else { } else {
props.ownProperties[props.name] = it props.ownProperties.setValue(props.name, it)
} }
update() update()
} }
@ -119,19 +115,20 @@ private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) {
update() update()
} }
if (actualItem is MetaItemNode) { val keys = buildSet {
val keys = buildSet { descriptor?.children?.filterNot {
(descriptorItem as? NodeDescriptor)?.items?.filterNot { it.key.startsWith("@") || it.value.hidden
it.key.startsWith("@") || it.value.hidden }?.forEach {
}?.forEach { add(NameToken(it.key))
add(NameToken(it.key))
}
ownProperty?.node?.items?.keys?.filterNot { it.body.startsWith("@") }?.let { addAll(it) }
} }
// Do not show nodes without visible children ownProperty?.items?.keys?.filterNot { it.body.startsWith("@") }?.let { addAll(it) }
if (keys.isEmpty()) return }
flexRow { flexRow {
css {
alignItems = Align.center
}
if(keys.isNotEmpty()) {
styledSpan { styledSpan {
css { css {
+TreeStyles.treeCaret +TreeStyles.treeCaret
@ -143,55 +140,17 @@ private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) {
onClickFunction = expanderClick onClickFunction = expanderClick
} }
} }
styledSpan {
css {
+TreeStyles.treeLabel
if (ownProperty == null) {
+TreeStyles.treeLabelInactive
}
}
+token
}
} }
if (expanded) { styledSpan {
flexColumn {
css {
+TreeStyles.tree
}
keys.forEach { token ->
styledDiv {
css {
+TreeStyles.treeItem
}
child(PropertyEditorItem) {
attrs {
this.key = props.name.toString()
this.ownProperties = props.ownProperties
this.allProperties = props.allProperties
this.name = props.name + token
this.descriptor = props.descriptor
}
}
//configEditor(props.root, props.name + token, props.descriptor, props.default)
}
}
}
}
} else {
flexRow {
css { css {
alignItems = Align.center +TreeStyles.treeLabel
} if (ownProperty == null) {
styledSpan { +TreeStyles.treeLabelInactive
css {
+TreeStyles.treeLabel
if (ownProperty == null) {
+TreeStyles.treeLabelInactive
}
} }
+token
} }
+token
}
if(!props.name.isEmpty() && descriptor?.valueRequirement != ValueRequirement.ABSENT) {
styledDiv { styledDiv {
css { css {
//+TreeStyles.resizeableInput //+TreeStyles.resizeableInput
@ -200,8 +159,8 @@ private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) {
} }
valueChooser( valueChooser(
props.name, props.name,
actualItem, actualMeta,
descriptorItem as? ValueDescriptor, descriptor,
valueChanged valueChanged
) )
} }
@ -232,14 +191,38 @@ private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) {
} }
} }
} }
} }
} }
if (expanded) {
flexColumn {
css {
+TreeStyles.tree
}
keys.forEach { token ->
styledDiv {
css {
+TreeStyles.treeItem
}
child(PropertyEditorItem) {
attrs {
this.key = props.name.toString()
this.ownProperties = props.ownProperties
this.allProperties = props.allProperties
this.name = props.name + token
this.descriptor = props.descriptor
}
}
//configEditor(props.root, props.name + token, props.descriptor, props.default)
}
}
}
}
} }
@JsExport @JsExport
public val PropertyEditor: FunctionalComponent<PropertyEditorProps> = functionalComponent("PropertyEditor") { props -> public val PropertyEditor: FunctionComponent<PropertyEditorProps> = functionalComponent("PropertyEditor") { props ->
child(PropertyEditorItem) { child(PropertyEditorItem) {
attrs { attrs {
this.key = "" this.key = ""
@ -254,10 +237,10 @@ public val PropertyEditor: FunctionalComponent<PropertyEditorProps> = functional
} }
public fun RBuilder.propertyEditor( public fun RBuilder.propertyEditor(
ownProperties: MutableItemProvider, ownProperties: MutableMetaProvider,
allProperties: ItemProvider? = ownProperties, allProperties: MetaProvider? = ownProperties,
updateFlow: Flow<Name>? = null, updateFlow: Flow<Name>? = null,
descriptor: NodeDescriptor? = null, descriptor: MetaDescriptor? = null,
scope: CoroutineScope? = null, scope: CoroutineScope? = null,
key: Any? = null, key: Any? = null,
expanded: Boolean? = null expanded: Boolean? = null
@ -276,8 +259,8 @@ public fun RBuilder.propertyEditor(
} }
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
private fun Config.flowUpdates(): Flow<Name> = callbackFlow { private fun ObservableMutableMeta.flowUpdates(): Flow<Name> = callbackFlow {
onChange(this) { name, _, _ -> onChange(this) { name ->
launch { launch {
send(name) send(name)
} }
@ -289,16 +272,16 @@ private fun Config.flowUpdates(): Flow<Name> = callbackFlow {
public fun RBuilder.configEditor( public fun RBuilder.configEditor(
config: Config, config: ObservableMutableMeta,
default: ItemProvider? = null, default: MetaProvider? = null,
descriptor: NodeDescriptor? = null, descriptor: MetaDescriptor? = null,
key: Any? = null, key: Any? = null,
scope: CoroutineScope? = null, scope: CoroutineScope? = null,
): Unit = propertyEditor(config, default, config.flowUpdates(), descriptor, scope, key = key) ): Unit = propertyEditor(config, default, config.flowUpdates(), descriptor, scope, key = key)
public fun Element.configEditor( public fun Element.configEditor(
config: Config, config: ObservableMutableMeta,
descriptor: NodeDescriptor? = null, descriptor: MetaDescriptor? = null,
default: Meta? = null, default: Meta? = null,
key: Any? = null, key: Any? = null,
scope: CoroutineScope? = null, scope: CoroutineScope? = null,

View File

@ -6,7 +6,7 @@ import kotlinx.html.InputType
import kotlinx.html.js.onChangeFunction import kotlinx.html.js.onChangeFunction
import org.w3c.dom.HTMLInputElement import org.w3c.dom.HTMLInputElement
import org.w3c.dom.events.Event import org.w3c.dom.events.Event
import react.FunctionalComponent import react.FunctionComponent
import react.dom.attrs import react.dom.attrs
import react.functionalComponent import react.functionalComponent
import react.useState import react.useState
@ -18,7 +18,7 @@ import styled.css
import styled.styledInput import styled.styledInput
@JsExport @JsExport
public val RangeValueChooser: FunctionalComponent<ValueChooserProps> = public val RangeValueChooser: FunctionComponent<ValueChooserProps> =
functionalComponent("RangeValueChooser") { props -> functionalComponent("RangeValueChooser") { props ->
var innerValue by useState(props.item.double) var innerValue by useState(props.item.double)
var rangeDisabled: Boolean by useState(props.item == null) var rangeDisabled: Boolean by useState(props.item == null)

View File

@ -21,7 +21,7 @@ public external interface ThreeCanvasProps : RProps {
public var selected: Name? public var selected: Name?
} }
public val ThreeCanvasComponent: FunctionalComponent<ThreeCanvasProps> = functionalComponent( public val ThreeCanvasComponent: FunctionComponent<ThreeCanvasProps> = functionalComponent(
"ThreeCanvasComponent" "ThreeCanvasComponent"
) { props -> ) { props ->
val elementRef = useRef<Element>(null) val elementRef = useRef<Element>(null)

View File

@ -107,7 +107,7 @@ private fun RBuilder.visionTree(props: ObjectTreeProps): Unit {
} }
@JsExport @JsExport
public val ObjectTree: FunctionalComponent<ObjectTreeProps> = functionalComponent("ObjectTree") { props -> public val ObjectTree: FunctionComponent<ObjectTreeProps> = functionalComponent("ObjectTree") { props ->
visionTree(props) visionTree(props)
} }

View File

@ -13,8 +13,12 @@ import org.w3c.dom.events.Event
import react.* import react.*
import react.dom.attrs import react.dom.attrs
import react.dom.option import react.dom.option
import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.descriptors.ValueDescriptor import space.kscience.dataforge.meta.boolean
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.meta.descriptors.allowedValues
import space.kscience.dataforge.meta.get
import space.kscience.dataforge.meta.string
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.values.* import space.kscience.dataforge.values.*
import space.kscience.visionforge.Colors import space.kscience.visionforge.Colors
@ -24,14 +28,15 @@ import styled.styledInput
import styled.styledSelect import styled.styledSelect
public external interface ValueChooserProps : RProps { public external interface ValueChooserProps : RProps {
public var item: MetaItem? public var item: Meta?
public var descriptor: ValueDescriptor? public var descriptor: MetaDescriptor?
//public var nullable: Boolean? //public var nullable: Boolean?
public var valueChanged: ((Value?) -> Unit)? public var valueChanged: ((Value?) -> Unit)?
} }
@JsExport @JsExport
public val StringValueChooser: FunctionalComponent<ValueChooserProps> = public val StringValueChooser: FunctionComponent<ValueChooserProps> =
functionalComponent("StringValueChooser") { props -> functionalComponent("StringValueChooser") { props ->
var value by useState(props.item.string ?: "") var value by useState(props.item.string ?: "")
val keyDown: (Event) -> Unit = { event -> val keyDown: (Event) -> Unit = { event ->
@ -46,7 +51,7 @@ public val StringValueChooser: FunctionalComponent<ValueChooserProps> =
value = (it.target as HTMLInputElement).value value = (it.target as HTMLInputElement).value
} }
styledInput(type = InputType.text) { styledInput(type = InputType.text) {
css{ css {
width = 100.pct width = 100.pct
} }
attrs { attrs {
@ -58,14 +63,14 @@ public val StringValueChooser: FunctionalComponent<ValueChooserProps> =
} }
@JsExport @JsExport
public val BooleanValueChooser: FunctionalComponent<ValueChooserProps> = public val BooleanValueChooser: FunctionComponent<ValueChooserProps> =
functionalComponent("BooleanValueChooser") { props -> functionalComponent("BooleanValueChooser") { props ->
val handleChange: (Event) -> Unit = { val handleChange: (Event) -> Unit = {
val newValue = (it.target as HTMLInputElement).checked val newValue = (it.target as HTMLInputElement).checked
props.valueChanged?.invoke(newValue.asValue()) props.valueChanged?.invoke(newValue.asValue())
} }
styledInput(type = InputType.checkBox) { styledInput(type = InputType.checkBox) {
css{ css {
width = 100.pct width = 100.pct
} }
attrs { attrs {
@ -77,7 +82,7 @@ public val BooleanValueChooser: FunctionalComponent<ValueChooserProps> =
} }
@JsExport @JsExport
public val NumberValueChooser: FunctionalComponent<ValueChooserProps> = public val NumberValueChooser: FunctionComponent<ValueChooserProps> =
functionalComponent("NumberValueChooser") { props -> functionalComponent("NumberValueChooser") { props ->
var innerValue by useState(props.item.string ?: "") var innerValue by useState(props.item.string ?: "")
val keyDown: (Event) -> Unit = { event -> val keyDown: (Event) -> Unit = { event ->
@ -95,7 +100,7 @@ public val NumberValueChooser: FunctionalComponent<ValueChooserProps> =
innerValue = (it.target as HTMLInputElement).value innerValue = (it.target as HTMLInputElement).value
} }
styledInput(type = InputType.number) { styledInput(type = InputType.number) {
css{ css {
width = 100.pct width = 100.pct
} }
attrs { attrs {
@ -116,7 +121,7 @@ public val NumberValueChooser: FunctionalComponent<ValueChooserProps> =
} }
@JsExport @JsExport
public val ComboValueChooser: FunctionalComponent<ValueChooserProps> = public val ComboValueChooser: FunctionComponent<ValueChooserProps> =
functionalComponent("ComboValueChooser") { props -> functionalComponent("ComboValueChooser") { props ->
var selected by useState(props.item.string ?: "") var selected by useState(props.item.string ?: "")
val handleChange: (Event) -> Unit = { val handleChange: (Event) -> Unit = {
@ -124,7 +129,7 @@ public val ComboValueChooser: FunctionalComponent<ValueChooserProps> =
props.valueChanged?.invoke(selected.asValue()) props.valueChanged?.invoke(selected.asValue())
} }
styledSelect { styledSelect {
css{ css {
width = 100.pct width = 100.pct
} }
props.descriptor?.allowedValues?.forEach { props.descriptor?.allowedValues?.forEach {
@ -141,10 +146,10 @@ public val ComboValueChooser: FunctionalComponent<ValueChooserProps> =
} }
@JsExport @JsExport
public val ColorValueChooser: FunctionalComponent<ValueChooserProps> = public val ColorValueChooser: FunctionComponent<ValueChooserProps> =
functionalComponent("ColorValueChooser") { props -> functionalComponent("ColorValueChooser") { props ->
var value by useState( var value by useState(
props.item.value?.let { value -> props.item?.value?.let { value ->
if (value.type == ValueType.NUMBER) Colors.rgbToString(value.int) if (value.type == ValueType.NUMBER) Colors.rgbToString(value.int)
else value.string else value.string
} ?: "#000000" } ?: "#000000"
@ -154,7 +159,7 @@ public val ColorValueChooser: FunctionalComponent<ValueChooserProps> =
props.valueChanged?.invoke(value.asValue()) props.valueChanged?.invoke(value.asValue())
} }
styledInput(type = InputType.color) { styledInput(type = InputType.color) {
css{ css {
width = 100.pct width = 100.pct
margin(0.px) margin(0.px)
} }
@ -166,11 +171,11 @@ public val ColorValueChooser: FunctionalComponent<ValueChooserProps> =
} }
@JsExport @JsExport
public val ValueChooser: FunctionalComponent<ValueChooserProps> = functionalComponent("ValueChooser") { props -> public val ValueChooser: FunctionComponent<ValueChooserProps> = functionalComponent("ValueChooser") { props ->
val rawInput by useState(false) val rawInput by useState(false)
val descriptor = props.descriptor val descriptor = props.descriptor
val type = descriptor?.type?.firstOrNull() val type = descriptor?.valueTypes?.firstOrNull()
when { when {
rawInput -> child(StringValueChooser, props) rawInput -> child(StringValueChooser, props)
@ -187,8 +192,8 @@ public val ValueChooser: FunctionalComponent<ValueChooserProps> = functionalComp
internal fun RBuilder.valueChooser( internal fun RBuilder.valueChooser(
name: Name, name: Name,
item: MetaItem?, item: Meta?,
descriptor: ValueDescriptor? = null, descriptor: MetaDescriptor? = null,
callback: (Value?) -> Unit, callback: (Value?) -> Unit,
) { ) {
child(ValueChooser) { child(ValueChooser) {

View File

@ -17,14 +17,13 @@ kotlin{
dependencies{ dependencies{
api(project(":ui:react")) api(project(":ui:react"))
//TODO replace by kotlin-wrappers //api("ru.mipt.npm:ring-ui:0.1.0")
api("ru.mipt.npm:ring-ui:0.1.0") api("org.jetbrains.kotlin-wrappers:kotlin-ring-ui")
implementation(npm("@jetbrains/icons", "3.14.1")) implementation(npm("@jetbrains/icons", "3.14.1"))
implementation(npm("@jetbrains/ring-ui", "4.0.7")) implementation(npm("@jetbrains/ring-ui", "4.0.7"))
implementation(npm("core-js","3.12.1")) implementation(npm("core-js","3.12.1"))
implementation(npm("file-saver", "2.0.2")) implementation(npm("file-saver", "2.0.2"))
compileOnly(npm("url-loader","4.1.1")) // compileOnly(npm("url-loader","4.1.1"))
compileOnly(npm("postcss-loader","5.2.0")) // compileOnly(npm("postcss-loader","5.2.0"))
compileOnly(npm("source-map-loader","2.0.1"))
} }

View File

@ -14,8 +14,7 @@ import space.kscience.dataforge.names.NameToken
import space.kscience.dataforge.names.isEmpty import space.kscience.dataforge.names.isEmpty
import space.kscience.dataforge.names.length import space.kscience.dataforge.names.length
import space.kscience.visionforge.VisionGroup import space.kscience.visionforge.VisionGroup
import space.kscience.visionforge.allProperties import space.kscience.visionforge.meta
import space.kscience.visionforge.ownProperties
import space.kscience.visionforge.react.ThreeCanvasComponent import space.kscience.visionforge.react.ThreeCanvasComponent
import space.kscience.visionforge.react.flexColumn import space.kscience.visionforge.react.flexColumn
import space.kscience.visionforge.react.flexRow import space.kscience.visionforge.react.flexRow
@ -70,7 +69,7 @@ public fun RBuilder.nameCrumbs(name: Name?, link: (Name) -> Unit): ReactElement
} }
@JsExport @JsExport
public val ThreeCanvasWithControls: FunctionalComponent<ThreeCanvasWithControlsProps> = public val ThreeCanvasWithControls: FunctionComponent<ThreeCanvasWithControlsProps> =
functionalComponent("ThreeViewWithControls") { props -> functionalComponent("ThreeViewWithControls") { props ->
var selected by useState { props.selected } var selected by useState { props.selected }
@ -120,23 +119,23 @@ public val ThreeCanvasWithControls: FunctionalComponent<ThreeCanvasWithControlsP
selectedVision?.let { vision -> selectedVision?.let { vision ->
styledDiv { styledDiv {
css{ css {
position = Position.absolute position = Position.absolute
top = 5.px top = 5.px
right = 5.px right = 5.px
width = 450.px width = 450.px
} }
Island{ Island {
IslandHeader{ IslandHeader {
attrs { attrs {
border = true border = true
} }
nameCrumbs(selected) { selected = it } nameCrumbs(selected) { selected = it }
} }
IslandContent{ IslandContent {
propertyEditor( propertyEditor(
ownProperties = vision.ownProperties, ownProperties = vision.meta(false, false, false),
allProperties = vision.allProperties(), allProperties = vision.meta(),
updateFlow = vision.propertyChanges, updateFlow = vision.propertyChanges,
descriptor = vision.descriptor, descriptor = vision.descriptor,
key = selected key = selected

View File

@ -7,16 +7,19 @@ import react.dom.render
import ringui.Island import ringui.Island
import ringui.SmartTabs import ringui.SmartTabs
import ringui.Tab import ringui.Tab
import space.kscience.dataforge.meta.descriptors.NodeDescriptor import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.visionforge.* import space.kscience.visionforge.Vision
import space.kscience.visionforge.getStyle
import space.kscience.visionforge.meta
import space.kscience.visionforge.react.flexColumn import space.kscience.visionforge.react.flexColumn
import space.kscience.visionforge.react.metaViewer import space.kscience.visionforge.react.metaViewer
import space.kscience.visionforge.react.propertyEditor import space.kscience.visionforge.react.propertyEditor
import space.kscience.visionforge.solid.SolidReference import space.kscience.visionforge.solid.SolidReference
import space.kscience.visionforge.styles
public fun RBuilder.ringPropertyEditor( public fun RBuilder.ringPropertyEditor(
vision: Vision, vision: Vision,
descriptor: NodeDescriptor? = vision.descriptor, descriptor: MetaDescriptor? = vision.descriptor,
key: Any? = null, key: Any? = null,
) { ) {
val styles = if (vision is SolidReference) { val styles = if (vision is SolidReference) {
@ -28,8 +31,8 @@ public fun RBuilder.ringPropertyEditor(
flexColumn { flexColumn {
Island("Properties") { Island("Properties") {
propertyEditor( propertyEditor(
ownProperties = vision.ownProperties, ownProperties = vision.meta(false,false,false),
allProperties = vision.allProperties(), allProperties = vision.meta(),
updateFlow = vision.propertyChanges, updateFlow = vision.propertyChanges,
descriptor = descriptor, descriptor = descriptor,
key = key key = key
@ -69,7 +72,7 @@ public fun RBuilder.ringPropertyEditor(
public fun Element.ringPropertyEditor( public fun Element.ringPropertyEditor(
item: Vision, item: Vision,
descriptor: NodeDescriptor? = item.descriptor, descriptor: MetaDescriptor? = item.descriptor,
): Unit = render(this) { ): Unit = render(this) {
ringPropertyEditor(item, descriptor = descriptor) ringPropertyEditor(item, descriptor = descriptor)
} }

View File

@ -15,6 +15,7 @@ import react.dom.button
import ringui.Island import ringui.Island
import ringui.SmartTabs import ringui.SmartTabs
import ringui.Tab import ringui.Tab
import space.kscience.dataforge.meta.descriptors.defaultNode
import space.kscience.dataforge.meta.withDefault import space.kscience.dataforge.meta.withDefault
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.visionforge.Vision import space.kscience.visionforge.Vision
@ -49,7 +50,7 @@ internal external interface CanvasControlsProps : RProps {
public var vision: Vision? public var vision: Vision?
} }
internal val CanvasControls: FunctionalComponent<CanvasControlsProps> = functionalComponent("CanvasControls") { props -> internal val CanvasControls: FunctionComponent<CanvasControlsProps> = functionalComponent("CanvasControls") { props ->
flexColumn { flexColumn {
flexRow { flexRow {
css { css {
@ -73,7 +74,7 @@ internal val CanvasControls: FunctionalComponent<CanvasControlsProps> = function
} }
propertyEditor( propertyEditor(
ownProperties = props.options, ownProperties = props.options,
allProperties = props.options.withDefault(Canvas3DOptions.descriptor.defaultMeta), allProperties = props.options.meta.withDefault(Canvas3DOptions.descriptor.defaultNode),
descriptor = Canvas3DOptions.descriptor, descriptor = Canvas3DOptions.descriptor,
expanded = false expanded = false
) )
@ -91,7 +92,7 @@ public external interface ThreeControlsProps : RProps {
} }
@JsExport @JsExport
public val ThreeControls: FunctionalComponent<ThreeControlsProps> = functionalComponent { props -> public val ThreeControls: FunctionComponent<ThreeControlsProps> = functionalComponent { props ->
SmartTabs("Tree") { SmartTabs("Tree") {
props.vision?.let { props.vision?.let {
Tab("Tree") { Tab("Tree") {

View File

@ -1,6 +1,8 @@
package space.kscience.visionforge package space.kscience.visionforge
import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.get
import space.kscience.dataforge.meta.number
import space.kscience.dataforge.values.ValueType import space.kscience.dataforge.values.ValueType
import space.kscience.dataforge.values.int import space.kscience.dataforge.values.int
import space.kscience.dataforge.values.string import space.kscience.dataforge.values.string
@ -190,25 +192,18 @@ public object Colors {
/** /**
* Convert color represented as Meta to string of format #rrggbb * Convert color represented as Meta to string of format #rrggbb
*/ */
fun fromMeta(item: MetaItem): String { fun fromMeta(meta: Meta): String = meta.value?.let { value ->
return when (item) { //if value is present, use it
is MetaItemNode -> { if (value.type == ValueType.NUMBER) {
val node = item.node rgbToString(value.int)
rgbToString( } else {
node[RED_KEY].number?.toByte()?.toUByte() ?: 0u, value.string
node[GREEN_KEY].number?.toByte()?.toUByte() ?: 0u,
node[BLUE_KEY].number?.toByte()?.toUByte() ?: 0u
)
}
is MetaItemValue -> {
if (item.value.type == ValueType.NUMBER) {
rgbToString(item.value.int)
} else {
item.value.string
}
}
} }
} } ?: rgbToString(
meta[RED_KEY].number?.toByte()?.toUByte() ?: 0u,
meta[GREEN_KEY].number?.toByte()?.toUByte() ?: 0u,
meta[BLUE_KEY].number?.toByte()?.toUByte() ?: 0u
)
/** /**
* Convert Int color to string of format #rrggbb * Convert Int color to string of format #rrggbb

View File

@ -1,7 +1,7 @@
package space.kscience.visionforge package space.kscience.visionforge
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.MetaBuilder import space.kscience.dataforge.meta.MutableMeta
import space.kscience.dataforge.meta.Scheme import space.kscience.dataforge.meta.Scheme
import space.kscience.dataforge.meta.Specification import space.kscience.dataforge.meta.Specification
import kotlin.properties.ReadOnlyProperty import kotlin.properties.ReadOnlyProperty
@ -27,7 +27,7 @@ public fun Vision.useStyle(reference: StyleReference) {
@VisionBuilder @VisionBuilder
public fun VisionGroup.style( public fun VisionGroup.style(
styleKey: String? = null, styleKey: String? = null,
builder: MetaBuilder.() -> Unit, builder: MutableMeta.() -> Unit,
): ReadOnlyProperty<Any?, StyleReference> = ReadOnlyProperty { _, property -> ): ReadOnlyProperty<Any?, StyleReference> = ReadOnlyProperty { _, property ->
val styleName = styleKey ?: property.name val styleName = styleKey ?: property.name
styleSheet.define(styleName, Meta(builder)) styleSheet.define(styleName, Meta(builder))

View File

@ -5,6 +5,7 @@ import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.NameToken import space.kscience.dataforge.names.NameToken
import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.asName
import space.kscience.dataforge.names.plus import space.kscience.dataforge.names.plus
import space.kscience.dataforge.values.asValue
import kotlin.jvm.JvmInline import kotlin.jvm.JvmInline
/** /**
@ -13,9 +14,9 @@ import kotlin.jvm.JvmInline
@JvmInline @JvmInline
public value class StyleSheet(private val owner: VisionGroup) { public value class StyleSheet(private val owner: VisionGroup) {
private val styleNode get() = owner.ownProperties[STYLESHEET_KEY].node private val styleNode: Meta? get() = owner.getOwnProperty(STYLESHEET_KEY)
public val items: Map<NameToken, Meta>? get() = styleNode?.items?.mapValues { it.value.node ?: Meta.EMPTY } public val items: Map<NameToken, Meta>? get() = styleNode?.items
public operator fun get(key: String): Meta? = owner.getStyle(key) public operator fun get(key: String): Meta? = owner.getStyle(key)
@ -23,7 +24,7 @@ public value class StyleSheet(private val owner: VisionGroup) {
* Define a style without notifying owner * Define a style without notifying owner
*/ */
public fun define(key: String, style: Meta?) { public fun define(key: String, style: Meta?) {
owner.setProperty(STYLESHEET_KEY + key, style) owner.setPropertyNode(STYLESHEET_KEY + key, style)
} }
/** /**
@ -40,7 +41,7 @@ public value class StyleSheet(private val owner: VisionGroup) {
/** /**
* Create and set a style * Create and set a style
*/ */
public operator fun set(key: String, builder: MetaBuilder.() -> Unit) { public operator fun set(key: String, builder: MutableMeta.() -> Unit) {
val newStyle = get(key)?.toMutableMeta()?.apply(builder) ?: Meta(builder) val newStyle = get(key)?.toMutableMeta()?.apply(builder) ?: Meta(builder)
set(key, newStyle.seal()) set(key, newStyle.seal())
} }
@ -70,9 +71,9 @@ internal fun Vision.styleChanged(key: String, oldStyle: Meta?, newStyle: Meta?)
* List of names of styles applied to this object. Order matters. Not inherited. * List of names of styles applied to this object. Order matters. Not inherited.
*/ */
public var Vision.styles: List<String> public var Vision.styles: List<String>
get() = ownProperties[Vision.STYLE_KEY]?.stringList ?: emptyList() get() = getOwnProperty(Vision.STYLE_KEY)?.stringList ?: emptyList()
set(value) { set(value) {
setProperty(Vision.STYLE_KEY, value) setPropertyValue(Vision.STYLE_KEY, value.map { it.asValue() }.asValue())
} }
/** /**
@ -85,7 +86,7 @@ public val VisionGroup.styleSheet: StyleSheet get() = StyleSheet(this)
* Add style name to the list of styles to be resolved later. The style with given name does not necessary exist at the moment. * Add style name to the list of styles to be resolved later. The style with given name does not necessary exist at the moment.
*/ */
public fun Vision.useStyle(name: String) { public fun Vision.useStyle(name: String) {
styles = (ownProperties[Vision.STYLE_KEY]?.stringList ?: emptyList()) + name styles = (getOwnProperty(Vision.STYLE_KEY)?.stringList ?: emptyList()) + name
} }
@ -93,13 +94,13 @@ public fun Vision.useStyle(name: String) {
* Find a style with given name for given [Vision]. The style is not necessary applied to this [Vision]. * Find a style with given name for given [Vision]. The style is not necessary applied to this [Vision].
*/ */
public tailrec fun Vision.getStyle(name: String): Meta? = public tailrec fun Vision.getStyle(name: String): Meta? =
ownProperties[StyleSheet.STYLESHEET_KEY + name].node ?: parent?.getStyle(name) getOwnProperty(StyleSheet.STYLESHEET_KEY + name) ?: parent?.getStyle(name)
/** /**
* Resolve an item in all style layers * Resolve an item in all style layers
*/ */
public fun Vision.getStyleItems(name: Name): List<MetaItem> = styles.mapNotNull { public fun Vision.getStyleItems(name: Name): List<Meta> = styles.mapNotNull {
getStyle(it)[name] getStyle(it)?.get(name)
} }

View File

@ -4,14 +4,15 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.MutableMeta
import space.kscience.dataforge.meta.descriptors.Described import space.kscience.dataforge.meta.descriptors.Described
import space.kscience.dataforge.meta.descriptors.NodeDescriptor import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.meta.descriptors.get import space.kscience.dataforge.meta.update
import space.kscience.dataforge.misc.Type import space.kscience.dataforge.misc.Type
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.asName
import space.kscience.dataforge.names.toName import space.kscience.dataforge.values.Value
import space.kscience.visionforge.Vision.Companion.TYPE import space.kscience.visionforge.Vision.Companion.TYPE
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext import kotlin.coroutines.EmptyCoroutineContext
@ -45,13 +46,13 @@ public interface Vision : Described, CoroutineScope {
inherit: Boolean = false, inherit: Boolean = false,
includeStyles: Boolean = true, includeStyles: Boolean = true,
includeDefaults: Boolean = true, includeDefaults: Boolean = true,
): MetaItem? ): Meta?
/** /**
* Get an intrinsic property of this Vision excluding any inheritance or defaults. In most cases should be the same as * Get an intrinsic property of this Vision excluding any inheritance or defaults. In most cases should be the same as
* `getProperty(name, false, false, false`. * `getProperty(name, false, false, false`.
*/ */
public fun getOwnProperty(name: Name): MetaItem? = getProperty( public fun getOwnProperty(name: Name): Meta? = getProperty(
name, name,
inherit = false, inherit = false,
includeStyles = false, includeStyles = false,
@ -59,9 +60,14 @@ public interface Vision : Described, CoroutineScope {
) )
/** /**
* Set the property value * Replace the property node. If [node] is null remove node and its descendants
*/ */
public fun setProperty(name: Name, item: MetaItem?, notify: Boolean = true) public fun setPropertyNode(name: Name, node: Meta?, notify: Boolean = true)
/**
* Set a value of specific property node
*/
public fun setPropertyValue(name: Name, value: Value?, notify: Boolean = true)
/** /**
* Flow of property invalidation events. It does not contain property values after invalidation since it is not clear * Flow of property invalidation events. It does not contain property values after invalidation since it is not clear
@ -77,9 +83,9 @@ public interface Vision : Described, CoroutineScope {
/** /**
* Update this vision using a dif represented by [VisionChange]. * Update this vision using a dif represented by [VisionChange].
*/ */
public fun update(change: VisionChange) public fun change(change: VisionChange)
override val descriptor: NodeDescriptor? override val descriptor: MetaDescriptor?
public companion object { public companion object {
public const val TYPE: String = "vision" public const val TYPE: String = "vision"
@ -89,11 +95,6 @@ public interface Vision : Described, CoroutineScope {
} }
} }
/**
* Root property node
*/
public val Vision.meta: Meta get() = ownProperties[Name.EMPTY]?.node ?: Meta.EMPTY
/** /**
* Subscribe on property updates. The subscription is bound to the given [scope] and canceled when the scope is canceled * Subscribe on property updates. The subscription is bound to the given [scope] and canceled when the scope is canceled
*/ */
@ -105,31 +106,26 @@ public fun Vision.onPropertyChange(scope: CoroutineScope, callback: suspend (Nam
/** /**
* Own properties, excluding inheritance, styles and descriptor * Own properties, excluding inheritance, styles and descriptor
*/ */
public val Vision.ownProperties: MutableItemProvider public fun Vision.meta(
get() = object : MutableItemProvider { inherit: Boolean = false,
override fun getItem(name: Name): MetaItem? = getOwnProperty(name) includeStyles: Boolean = true,
override fun setItem(name: Name, item: MetaItem?): Unit = setProperty(name, item)
}
/**
* Convenient accessor for all properties of a vision.
* @param inherit - inherit property value from the parent by default. If null, inheritance is inferred from descriptor
*/
public fun Vision.allProperties(
inherit: Boolean? = null,
includeStyles: Boolean? = null,
includeDefaults: Boolean = true, includeDefaults: Boolean = true,
): MutableItemProvider = object : MutableItemProvider { ): MutableMeta = VisionProperties(this, Name.EMPTY, inherit, includeStyles, includeDefaults)
override fun getItem(name: Name): MetaItem? = getProperty(
name,
inherit = inherit ?: (descriptor?.get(name)?.inherited == true),
includeStyles = includeStyles ?: (descriptor?.get(name)?.usesStyles != false),
includeDefaults = includeDefaults
)
override fun setItem(name: Name, item: MetaItem?): Unit = setProperty(name, item) public fun Vision.configure(target: Name = Name.EMPTY, block: MutableMeta.() -> Unit): Unit {
VisionProperties(this, target).apply(block)
} }
public fun Vision.configure(meta: Meta) {
configure(Name.EMPTY) {
update(meta)
}
}
public fun Vision.configure(block: MutableMeta.() -> Unit): Unit = configure(Meta(block))
public fun Vision.getOwnProperty(key: String): Meta? = getOwnProperty(Name.parse(key))
/** /**
* Get [Vision] property using key as a String * Get [Vision] property using key as a String
*/ */
@ -138,18 +134,20 @@ public fun Vision.getProperty(
inherit: Boolean = false, inherit: Boolean = false,
includeStyles: Boolean = true, includeStyles: Boolean = true,
includeDefaults: Boolean = true, includeDefaults: Boolean = true,
): MetaItem? = getProperty(key.toName(), inherit, includeStyles, includeDefaults) ): Meta? = getProperty(Name.parse(key), inherit, includeStyles, includeDefaults)
/** /**
* A convenience method to pair [getProperty] * A convenience method to set property node or value. If Item is null, then node is removed, not a value
*/ */
public fun Vision.setProperty(key: Name, item: Any?) { public fun Vision.setProperty(name: Name, item: Any?) {
setProperty(key, MetaItem.of(item)) when (item) {
null -> setPropertyNode(name, null)
is Meta -> setPropertyNode(name, item)
is Value -> setPropertyValue(name, item)
}
} }
/** public fun Vision.setPropertyNode(key: String, item: Any?) {
* A convenience method to pair [getProperty] setProperty(Name.parse(key), item)
*/
public fun Vision.setProperty(key: String, item: Any?) {
setProperty(key.toName(), MetaItem.of(item))
} }

View File

@ -7,21 +7,18 @@ import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient import kotlinx.serialization.Transient
import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.*
import space.kscience.dataforge.meta.descriptors.NodeDescriptor import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.meta.descriptors.defaultNode
import space.kscience.dataforge.meta.descriptors.value
import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.asName
import space.kscience.dataforge.names.plus import space.kscience.dataforge.names.plus
import space.kscience.dataforge.values.Null import space.kscience.dataforge.values.Value
import space.kscience.dataforge.values.ValueType import space.kscience.dataforge.values.ValueType
import space.kscience.visionforge.Vision.Companion.STYLE_KEY import space.kscience.visionforge.Vision.Companion.STYLE_KEY
import kotlin.jvm.Synchronized import kotlin.jvm.Synchronized
internal data class PropertyListener(
val owner: Any? = null,
val action: (name: Name) -> Unit,
)
/** /**
* A full base implementation for a [Vision] * A full base implementation for a [Vision]
* @param properties Object own properties excluding styles and inheritance * @param properties Object own properties excluding styles and inheritance
@ -29,14 +26,17 @@ internal data class PropertyListener(
@Serializable @Serializable
@SerialName("vision") @SerialName("vision")
public open class VisionBase( public open class VisionBase(
override @Transient var parent: VisionGroup? = null, @Transient override var parent: VisionGroup? = null,
protected var properties: MutableItemProvider? = null @Serializable(MutableMetaSerializer::class)
protected var properties: MutableMeta? = null
) : Vision { ) : Vision {
//protected val observableProperties: ObservableMutableMeta by lazy { properties.asObservable() }
@Synchronized @Synchronized
protected fun getOrCreateProperties(): MutableItemProvider { protected fun getOrCreateProperties(): MutableMeta {
if (properties == null) { if (properties == null) {
val newProperties = MetaBuilder() val newProperties = MutableMeta()
properties = newProperties properties = newProperties
} }
return properties!! return properties!!
@ -45,18 +45,14 @@ public open class VisionBase(
/** /**
* A fast accessor method to get own property (no inheritance or styles * A fast accessor method to get own property (no inheritance or styles
*/ */
override fun getOwnProperty(name: Name): MetaItem? = if (name == Name.EMPTY) { override fun getOwnProperty(name: Name): Meta? = properties?.getMeta(name)
properties?.rootItem
} else {
properties?.getItem(name)
}
override fun getProperty( override fun getProperty(
name: Name, name: Name,
inherit: Boolean, inherit: Boolean,
includeStyles: Boolean, includeStyles: Boolean,
includeDefaults: Boolean, includeDefaults: Boolean,
): MetaItem? = if (!inherit && !includeStyles && !includeDefaults) { ): Meta? = if (!inherit && !includeStyles && !includeDefaults) {
getOwnProperty(name) getOwnProperty(name)
} else { } else {
buildList { buildList {
@ -68,22 +64,32 @@ public open class VisionBase(
add(parent?.getProperty(name, inherit, includeStyles, includeDefaults)) add(parent?.getProperty(name, inherit, includeStyles, includeDefaults))
} }
if (includeDefaults) { if (includeDefaults) {
add(descriptor?.defaultMeta?.get(name)) add(descriptor?.defaultNode?.get(name))
} }
}.merge() }.merge()
} }
override fun setProperty(name: Name, item: MetaItem?, notify: Boolean) { override fun setPropertyNode(name: Name, node: Meta?, notify: Boolean) {
val oldItem = properties?.getItem(name) val oldItem = properties?.get(name)
if(oldItem!= item) { if (oldItem != node) {
getOrCreateProperties().setItem(name, item) getOrCreateProperties().setMeta(name, node)
if (notify) { if (notify) {
invalidateProperty(name) invalidateProperty(name)
} }
} }
} }
override val descriptor: NodeDescriptor? get() = null override fun setPropertyValue(name: Name, value: Value?, notify: Boolean) {
val oldItem = properties?.get(name)?.value
if (oldItem != value) {
getOrCreateProperties()[name] = value
if (notify) {
invalidateProperty(name)
}
}
}
override val descriptor: MetaDescriptor? get() = null
private suspend fun updateStyles(names: List<String>) { private suspend fun updateStyles(names: List<String>) {
names.mapNotNull { getStyle(it) }.asSequence() names.mapNotNull { getStyle(it) }.asSequence()
@ -111,31 +117,23 @@ public open class VisionBase(
} }
} }
override fun update(change: VisionChange) { override fun change(change: VisionChange) {
change.properties?.let { change.properties?.let {
updateProperties(Name.EMPTY, it.asMetaItem()) updateProperties(Name.EMPTY, it)
} }
} }
public companion object { public companion object {
public val descriptor: NodeDescriptor = NodeDescriptor { public val descriptor: MetaDescriptor = MetaDescriptor {
value(STYLE_KEY) { value(STYLE_KEY, ValueType.STRING) {
type(ValueType.STRING)
multiple = true multiple = true
} }
} }
public fun Vision.updateProperties(at: Name, item: MetaItem) { public fun Vision.updateProperties(at: Name, item: Meta) {
when (item) { setPropertyValue(at, item.value)
is MetaItemValue -> { item.items.forEach { (token, item) ->
if (item.value == Null) { updateProperties(at + token, item)
setProperty(at, null)
} else
setProperty(at, item)
}
is MetaItemNode -> item.node.items.forEach { (token, childItem) ->
updateProperties(at + token, childItem)
}
} }
} }

View File

@ -21,7 +21,7 @@ public class VisionChangeBuilder : VisionContainerBuilder<Vision> {
private var reset: Boolean = false private var reset: Boolean = false
private var vision: Vision? = null private var vision: Vision? = null
private val propertyChange = MetaBuilder() private val propertyChange = MutableMeta()
private val children: HashMap<Name, VisionChangeBuilder> = HashMap() private val children: HashMap<Name, VisionChangeBuilder> = HashMap()
public fun isEmpty(): Boolean = propertyChange.isEmpty() && propertyChange.isEmpty() && children.isEmpty() public fun isEmpty(): Boolean = propertyChange.isEmpty() && propertyChange.isEmpty() && children.isEmpty()
@ -30,17 +30,17 @@ public class VisionChangeBuilder : VisionContainerBuilder<Vision> {
private fun getOrPutChild(visionName: Name): VisionChangeBuilder = private fun getOrPutChild(visionName: Name): VisionChangeBuilder =
children.getOrPut(visionName) { VisionChangeBuilder() } children.getOrPut(visionName) { VisionChangeBuilder() }
public fun propertyChanged(visionName: Name, propertyName: Name, item: MetaItem?) { public fun propertyChanged(visionName: Name, propertyName: Name, item: Meta?) {
if (visionName == Name.EMPTY) { if (visionName == Name.EMPTY) {
//Write property removal as [Null] //Write property removal as [Null]
propertyChange[propertyName] = (item ?: Null.asMetaItem()) propertyChange[propertyName] = (item ?: Meta(Null))
} else { } else {
getOrPutChild(visionName).propertyChanged(Name.EMPTY, propertyName, item) getOrPutChild(visionName).propertyChanged(Name.EMPTY, propertyName, item)
} }
} }
override fun set(name: Name?, child: Vision?) { override fun set(name: Name?, child: Vision?) {
if(name == null) error("Static children are not allowed in VisionChange") if (name == null) error("Static children are not allowed in VisionChange")
getOrPutChild(name).apply { getOrPutChild(name).apply {
vision = child vision = child
reset = vision == null reset = vision == null
@ -90,7 +90,7 @@ private fun CoroutineScope.collectChange(
//Collect properties change //Collect properties change
source.onPropertyChange(this) { propertyName -> source.onPropertyChange(this) { propertyName ->
val newItem = source.ownProperties[propertyName] val newItem = source.getOwnProperty(propertyName)
collector().propertyChanged(name, propertyName, newItem) collector().propertyChanged(name, propertyName, newItem)
} }

View File

@ -75,12 +75,12 @@ public interface MutableVisionGroup : VisionGroup, VisionContainerBuilder<Vision
public val structureChanges: Flow<StructureChange> public val structureChanges: Flow<StructureChange>
} }
public operator fun <V : Vision> VisionContainer<V>.get(str: String): V? = get(str.toName()) public operator fun <V : Vision> VisionContainer<V>.get(str: String): V? = get(Name.parse(str))
public operator fun <V : Vision> VisionContainerBuilder<V>.set(token: NameToken, child: V?): Unit = public operator fun <V : Vision> VisionContainerBuilder<V>.set(token: NameToken, child: V?): Unit =
set(token.asName(), child) set(token.asName(), child)
public operator fun <V : Vision> VisionContainerBuilder<V>.set(key: String?, child: V?): Unit = public operator fun <V : Vision> VisionContainerBuilder<V>.set(key: String?, child: V?): Unit =
set(key?.toName(), child) set(key?.let(Name::parse), child)
public fun MutableVisionGroup.removeAll(): Unit = children.keys.map { it.asName() }.forEach { this[it] = null } public fun MutableVisionGroup.removeAll(): Unit = children.keys.map { it.asName() }.forEach { this[it] = null }

View File

@ -131,15 +131,15 @@ public open class VisionGroupBase(
} }
} }
override fun update(change: VisionChange) { override fun change(change: VisionChange) {
change.children?.forEach { (name, change) -> change.children?.forEach { (name, change) ->
when { when {
change.delete -> set(name, null) change.delete -> set(name, null)
change.vision != null -> set(name, change.vision) change.vision != null -> set(name, change.vision)
else -> get(name)?.update(change) else -> get(name)?.change(change)
} }
} }
super.update(change) super.change(change)
} }
} }

View File

@ -8,12 +8,10 @@ import kotlinx.serialization.modules.polymorphic
import kotlinx.serialization.modules.subclass import kotlinx.serialization.modules.subclass
import space.kscience.dataforge.context.* import space.kscience.dataforge.context.*
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.descriptors.NodeDescriptor import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.meta.node
import space.kscience.dataforge.meta.toJson import space.kscience.dataforge.meta.toJson
import space.kscience.dataforge.meta.toMetaItem import space.kscience.dataforge.meta.toMeta
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.toName
import kotlin.reflect.KClass import kotlin.reflect.KClass
public class VisionManager(meta: Meta) : AbstractPlugin(meta) { public class VisionManager(meta: Meta) : AbstractPlugin(meta) {
@ -48,12 +46,11 @@ public class VisionManager(meta: Meta) : AbstractPlugin(meta) {
jsonFormat.encodeToJsonElement(visionSerializer, vision) jsonFormat.encodeToJsonElement(visionSerializer, vision)
//TODO remove double transformation with dedicated Meta serial format //TODO remove double transformation with dedicated Meta serial format
public fun decodeFromMeta(meta: Meta, descriptor: NodeDescriptor? = null): Vision = public fun decodeFromMeta(meta: Meta, descriptor: MetaDescriptor? = null): Vision =
decodeFromJson(meta.toJson(descriptor)) decodeFromJson(meta.toJson(descriptor))
public fun encodeToMeta(vision: Vision, descriptor: NodeDescriptor? = null): Meta = public fun encodeToMeta(vision: Vision, descriptor: MetaDescriptor? = null): Meta =
encodeToJsonElement(vision).toMetaItem(descriptor).node encodeToJsonElement(vision).toMeta(descriptor)
?: error("Expected node, but value found. Check your serializer!")
public companion object : PluginFactory<VisionManager> { public companion object : PluginFactory<VisionManager> {
override val tag: PluginTag = PluginTag(name = "vision", group = PluginTag.DATAFORGE_GROUP) override val tag: PluginTag = PluginTag(name = "vision", group = PluginTag.DATAFORGE_GROUP)
@ -89,7 +86,7 @@ public abstract class VisionPlugin(meta: Meta = Meta.EMPTY) : AbstractPlugin(met
protected abstract val visionSerializersModule: SerializersModule protected abstract val visionSerializersModule: SerializersModule
override fun content(target: String): Map<Name, Any> = when (target) { override fun content(target: String): Map<Name, Any> = when (target) {
VisionManager.VISION_SERIALIZER_MODULE_TARGET -> mapOf(tag.toString().toName() to visionSerializersModule) VisionManager.VISION_SERIALIZER_MODULE_TARGET -> mapOf(Name.parse(tag.toString()) to visionSerializersModule)
else -> super.content(target) else -> super.content(target)
} }
} }

View File

@ -0,0 +1,38 @@
package space.kscience.visionforge
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.MutableMeta
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.NameToken
import space.kscience.dataforge.names.plus
import space.kscience.dataforge.values.Value
internal class VisionProperties(
val vision: Vision,
val rootName: Name,
val inherit: Boolean = false,
val includeStyles: Boolean = true,
val includeDefaults: Boolean = true,
) : MutableMeta {
override val items: Map<NameToken, MutableMeta>
get() = vision.getProperty(rootName, inherit, includeStyles, includeDefaults)?.items?.mapValues {
VisionProperties(vision, rootName + it.key, inherit, includeStyles, includeDefaults)
} ?: emptyMap()
override var value: Value?
get() = vision.getProperty(rootName, inherit, includeStyles, includeDefaults)?.value
set(value) {
vision.setPropertyValue(rootName, value)
}
override fun getOrCreate(name: Name): MutableMeta = VisionProperties(vision, rootName + name)
override fun setMeta(name: Name, node: Meta?) {
vision.setPropertyNode(rootName + name, node)
}
override fun equals(other: Any?): Boolean = Meta.equals(this, other as? Meta)
override fun hashCode(): Int = Meta.hashCode(this)
override fun toString(): String = Meta.toString(this)
}

View File

@ -1,32 +1,49 @@
package space.kscience.visionforge package space.kscience.visionforge
import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.Configurable
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.ObservableMutableMeta
import space.kscience.dataforge.meta.get
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.values.Value
/** /**
* Property containers are used to create a symmetric behaviors for vision properties and style builders * Property containers are used to create a symmetric behaviors for vision properties and style builders
*/ */
public interface VisionPropertyContainer<out V: Vision> { public interface VisionPropertyContainer<out V : Vision> {
public fun getProperty( public fun getProperty(
name: Name, name: Name,
inherit: Boolean = false, inherit: Boolean = false,
includeStyles: Boolean = true, includeStyles: Boolean = true,
includeDefaults: Boolean = true, includeDefaults: Boolean = true,
): MetaItem? ): Meta?
public fun setProperty(name: Name, item: MetaItem?, notify: Boolean = true) /**
* Replace the property node. If [node] is null remove node and its descendants
*/
public fun setPropertyNode(name: Name, node: Meta?, notify: Boolean = true)
/**
* Set a value of specific property node
*/
public fun setPropertyValue(name: Name, value: Value?, notify: Boolean = true)
} }
public open class SimpleVisionPropertyContainer<out V: Vision>(protected val config: ObservableMeta): VisionPropertyContainer<V>{ public open class SimpleVisionPropertyContainer<out V : Vision>(
override val meta: ObservableMutableMeta,
) : VisionPropertyContainer<V>, Configurable {
override fun getProperty( override fun getProperty(
name: Name, name: Name,
inherit: Boolean, inherit: Boolean,
includeStyles: Boolean, includeStyles: Boolean,
includeDefaults: Boolean includeDefaults: Boolean
): MetaItem? = config[name] ): Meta? = meta[name]
override fun setProperty(name: Name, item: MetaItem?, notify: Boolean) { override fun setPropertyNode(name: Name, node: Meta?, notify: Boolean) {
config[name] = item this.meta.setMeta(name, node)
} }
override fun setPropertyValue(name: Name, value: Value?, notify: Boolean) {
this.meta.setValue(name, value)
}
} }

View File

@ -2,12 +2,13 @@ package space.kscience.visionforge.html
import kotlinx.html.* import kotlinx.html.*
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.MetaBuilder
import space.kscience.dataforge.meta.MetaSerializer import space.kscience.dataforge.meta.MetaSerializer
import space.kscience.dataforge.meta.MutableMeta
import space.kscience.dataforge.meta.isEmpty import space.kscience.dataforge.meta.isEmpty
import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.toName import space.kscience.dataforge.names.NameToken
import space.kscience.dataforge.names.asName
import space.kscience.visionforge.Vision import space.kscience.visionforge.Vision
import space.kscience.visionforge.VisionManager import space.kscience.visionforge.VisionManager
import kotlin.collections.set import kotlin.collections.set
@ -25,7 +26,7 @@ public class VisionOutput @PublishedApi internal constructor(public val manager:
//TODO expose a way to define required plugins. //TODO expose a way to define required plugins.
public inline fun meta(block: MetaBuilder.() -> Unit) { public inline fun meta(block: MutableMeta.() -> Unit) {
this.meta = Meta(block) this.meta = Meta(block)
} }
} }
@ -36,7 +37,7 @@ public class VisionOutput @PublishedApi internal constructor(public val manager:
@VisionDSL @VisionDSL
public abstract class VisionTagConsumer<R>( public abstract class VisionTagConsumer<R>(
private val root: TagConsumer<R>, private val root: TagConsumer<R>,
public val manager:VisionManager, public val manager: VisionManager,
private val idPrefix: String? = null, private val idPrefix: String? = null,
) : TagConsumer<R> by root { ) : TagConsumer<R> by root {
@ -94,11 +95,11 @@ public abstract class VisionTagConsumer<R>(
public inline fun <T> TagConsumer<T>.vision( public inline fun <T> TagConsumer<T>.vision(
name: String = DEFAULT_VISION_NAME, name: String = DEFAULT_VISION_NAME,
visionProvider: VisionOutput.() -> Vision, visionProvider: VisionOutput.() -> Vision,
): T = vision(name.toName(), visionProvider) ): T = vision(Name.parse(name), visionProvider)
public fun <T> TagConsumer<T>.vision( public fun <T> TagConsumer<T>.vision(
vision: Vision, vision: Vision,
): T = vision("vision[${vision.hashCode()}]".toName(), vision) ): T = vision(NameToken("vision", vision.hashCode().toString()).asName(), vision)
/** /**
* Process the resulting object produced by [TagConsumer] * Process the resulting object produced by [TagConsumer]

View File

@ -1,18 +1,20 @@
package space.kscience.visionforge package space.kscience.visionforge
import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.Laminate
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.boolean
import space.kscience.dataforge.meta.isLeaf
import space.kscience.dataforge.values.asValue import space.kscience.dataforge.values.asValue
@DslMarker @DslMarker
public annotation class VisionBuilder public annotation class VisionBuilder
public fun List<MetaItem?>.merge(): MetaItem? = when (val first = firstOrNull { it != null }) { public fun List<Meta?>.merge(): Meta? {
null -> null val first = firstOrNull { it != null }
is MetaItemValue -> first //fast search for first entry if it is value return when {
is MetaItemNode -> { first == null -> null
//merge nodes if first encountered node is meta first.isLeaf -> first //fast search for first entry if it is value
val laminate: Laminate = Laminate(mapNotNull { it.node }) else -> Laminate(filterNotNull()) //merge nodes if first encountered node is meta
MetaItemNode(laminate)
} }
} }
@ -21,8 +23,4 @@ public fun List<MetaItem?>.merge(): MetaItem? = when (val first = firstOrNull {
*/ */
public var Vision.visible: Boolean? public var Vision.visible: Boolean?
get() = getProperty(Vision.VISIBLE_KEY).boolean get() = getProperty(Vision.VISIBLE_KEY).boolean
set(value) = setProperty(Vision.VISIBLE_KEY, value?.asValue()) set(value) = setPropertyValue(Vision.VISIBLE_KEY, value?.asValue())
public fun Vision.configure(meta: Meta?): Unit = update(VisionChange(properties = meta))
public fun Vision.configure(block: MetaBuilder.() -> Unit): Unit = configure(Meta(block))

View File

@ -2,11 +2,11 @@ package space.kscience.visionforge
import space.kscience.dataforge.meta.Scheme import space.kscience.dataforge.meta.Scheme
import space.kscience.dataforge.meta.SchemeSpec import space.kscience.dataforge.meta.SchemeSpec
import space.kscience.dataforge.meta.asObservable import space.kscience.dataforge.meta.descriptors.Described
import space.kscience.dataforge.meta.descriptors.NodeDescriptor import space.kscience.dataforge.meta.descriptors.MetaDescriptorBuilder
import space.kscience.dataforge.meta.descriptors.NodeDescriptorBuilder import space.kscience.dataforge.meta.descriptors.item
import space.kscience.dataforge.meta.descriptors.ValueDescriptorBuilder import space.kscience.dataforge.meta.descriptors.value
import space.kscience.dataforge.meta.toMutableMeta import space.kscience.dataforge.names.Name
import space.kscience.dataforge.values.ValueType import space.kscience.dataforge.values.ValueType
import kotlin.reflect.KProperty1 import kotlin.reflect.KProperty1
import kotlin.reflect.typeOf import kotlin.reflect.typeOf
@ -14,55 +14,53 @@ import kotlin.reflect.typeOf
/** /**
* TODO to be moved into the core * TODO to be moved into the core
*/ */
public inline fun <S : Scheme, reified T> NodeDescriptorBuilder.value( public inline fun <S : Scheme, reified T> MetaDescriptorBuilder.value(
property: KProperty1<S, T>, property: KProperty1<S, T>,
noinline block: ValueDescriptorBuilder.() -> Unit = {}, noinline block: MetaDescriptorBuilder.() -> Unit = {},
) { ) {
when (typeOf<T>()) { when (typeOf<T>()) {
typeOf<Number>(), typeOf<Int>(), typeOf<Double>(), typeOf<Short>(), typeOf<Long>(), typeOf<Float>() -> typeOf<Number>(), typeOf<Int>(), typeOf<Double>(), typeOf<Short>(), typeOf<Long>(), typeOf<Float>() ->
value(property.name) { value(property.name, ValueType.NUMBER) {
type(ValueType.NUMBER)
block() block()
} }
typeOf<Number?>(), typeOf<Int?>(), typeOf<Double?>(), typeOf<Short?>(), typeOf<Long?>(), typeOf<Float?>() -> typeOf<Number?>(), typeOf<Int?>(), typeOf<Double?>(), typeOf<Short?>(), typeOf<Long?>(), typeOf<Float?>() ->
value(property.name) { value(property.name, ValueType.NUMBER) {
type(ValueType.NUMBER)
block() block()
} }
typeOf<Boolean>() -> value(property.name) { typeOf<Boolean>() -> value(property.name, ValueType.BOOLEAN) {
type(ValueType.BOOLEAN)
block() block()
} }
typeOf<List<Number>>(), typeOf<List<Int>>(), typeOf<List<Double>>(), typeOf<List<Short>>(), typeOf<List<Long>>(), typeOf<List<Float>>(), typeOf<List<Number>>(), typeOf<List<Int>>(), typeOf<List<Double>>(), typeOf<List<Short>>(), typeOf<List<Long>>(), typeOf<List<Float>>(),
typeOf<IntArray>(), typeOf<DoubleArray>(), typeOf<ShortArray>(), typeOf<LongArray>(), typeOf<FloatArray>(), typeOf<IntArray>(), typeOf<DoubleArray>(), typeOf<ShortArray>(), typeOf<LongArray>(), typeOf<FloatArray>(),
-> value(property.name) { -> value(property.name, ValueType.NUMBER) {
type(ValueType.NUMBER)
multiple = true multiple = true
block() block()
} }
typeOf<String>() -> value(property.name) { typeOf<String>() -> value(property.name, ValueType.STRING) {
type(ValueType.STRING)
block() block()
} }
typeOf<List<String>>(), typeOf<Array<String>>() -> value(property.name) { typeOf<List<String>>(), typeOf<Array<String>>() -> value(property.name, ValueType.STRING) {
type(ValueType.STRING)
multiple = true multiple = true
block() block()
} }
else -> value(property.name, block) else -> item(property.name, block)
} }
} }
public fun NodeDescriptor.copy(block: NodeDescriptorBuilder.() -> Unit = {}): NodeDescriptor { public fun MetaDescriptorBuilder.item(
return NodeDescriptorBuilder(toMeta().toMutableMeta().asObservable()).apply(block) key: String,
described: Described,
block: MetaDescriptorBuilder.() -> Unit = {},
) {
described.descriptor?.let {
item(Name.parse(key), it, block)
}
} }
public inline fun <S : Scheme, reified T : Scheme> NodeDescriptorBuilder.scheme( public inline fun <S : Scheme, reified T : Scheme> MetaDescriptorBuilder.scheme(
property: KProperty1<S, T>, property: KProperty1<S, T>,
spec: SchemeSpec<T>, spec: SchemeSpec<T>,
noinline block: NodeDescriptorBuilder.() -> Unit = {}, noinline block: MetaDescriptorBuilder.() -> Unit = {},
) { ) {
spec.descriptor?.let { descriptor -> item(property.name, spec, block)
item(property.name, descriptor.copy(block)) }
}
}

View File

@ -0,0 +1,93 @@
package space.kscience.visionforge
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.transformations.MetaConverter
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.values.Value
import space.kscience.dataforge.values.number
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
public fun Vision.propertyNode(
name: Name? = null,
inherit: Boolean = false,
includeStyles: Boolean = true,
includeDefaults: Boolean = true,
): ReadWriteProperty<Any?, Meta?> = object : ReadWriteProperty<Any?, Meta?> {
override fun getValue(thisRef: Any?, property: KProperty<*>): Meta? =
getProperty(name ?: Name.parse(property.name), inherit, includeStyles, includeDefaults)
override fun setValue(thisRef: Any?, property: KProperty<*>, value: Meta?) {
setPropertyNode(name ?: Name.parse(property.name), value)
}
}
public fun <T> Vision.propertyNode(
converter: MetaConverter<T>,
name: Name? = null,
inherit: Boolean = false,
includeStyles: Boolean = true,
includeDefaults: Boolean = true,
): ReadWriteProperty<Any?, T?> = object : ReadWriteProperty<Any?, T?> {
override fun getValue(thisRef: Any?, property: KProperty<*>): T? = getProperty(
name ?: Name.parse(property.name),
inherit,
includeStyles,
includeDefaults
)?.let(converter::metaToObject)
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
setPropertyNode(name ?: Name.parse(property.name), value?.let(converter::objectToMeta))
}
}
public fun Vision.propertyValue(
name: Name? = null,
inherit: Boolean = false,
includeStyles: Boolean = true,
includeDefaults: Boolean = true,
): ReadWriteProperty<Any?, Value?> = object : ReadWriteProperty<Any?, Value?> {
override fun getValue(thisRef: Any?, property: KProperty<*>): Value? =
getProperty(name ?: Name.parse(property.name), inherit, includeStyles, includeDefaults)?.value
override fun setValue(thisRef: Any?, property: KProperty<*>, value: Value?) {
setPropertyValue(name ?: Name.parse(property.name), value)
}
}
public fun <T> Vision.propertyValue(
name: Name? = null,
inherit: Boolean = false,
includeStyles: Boolean = true,
includeDefaults: Boolean = true,
setter: (T) -> Value? = { it?.let(Value::of) },
getter: (Value?) -> T,
): ReadWriteProperty<Any?, T> = object : ReadWriteProperty<Any?, T> {
override fun getValue(thisRef: Any?, property: KProperty<*>): T = getProperty(
name ?: Name.parse(property.name),
inherit,
includeStyles,
includeDefaults
)?.value.let(getter)
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
setPropertyValue(name ?: Name.parse(property.name), value?.let(setter))
}
}
public fun Vision.numberProperty(
name: Name? = null,
inherit: Boolean = false,
includeStyles: Boolean = true,
includeDefaults: Boolean = true
): ReadWriteProperty<Any?, Number?> = propertyValue(name, inherit, includeStyles, includeDefaults) { it?.number }
public fun Vision.numberProperty(
name: Name? = null,
inherit: Boolean = false,
includeStyles: Boolean = true,
includeDefaults: Boolean = true,
default: () -> Number
): ReadWriteProperty<Any?, Number> = propertyValue(name, inherit, includeStyles, includeDefaults) {
it?.number ?: default()
}

View File

@ -2,87 +2,63 @@ package space.kscience.visionforge
import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.*
import space.kscience.dataforge.meta.descriptors.* import space.kscience.dataforge.meta.descriptors.*
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.asName
import space.kscience.dataforge.values.ValueType
import space.kscience.dataforge.values.asValue import space.kscience.dataforge.values.asValue
private const val INHERITED_DESCRIPTOR_ATTRIBUTE = "inherited" private const val INHERITED_DESCRIPTOR_ATTRIBUTE = "inherited"
private const val STYLE_DESCRIPTOR_ATTRIBUTE = "useStyles" private const val STYLE_DESCRIPTOR_ATTRIBUTE = "useStyles"
public val ItemDescriptor.inherited: Boolean public val MetaDescriptor.inherited: Boolean
get() = attributes[INHERITED_DESCRIPTOR_ATTRIBUTE].boolean ?: false get() = attributes[INHERITED_DESCRIPTOR_ATTRIBUTE].boolean ?: false
public var ItemDescriptorBuilder.inherited: Boolean public var MetaDescriptorBuilder.inherited: Boolean
get() = attributes[INHERITED_DESCRIPTOR_ATTRIBUTE].boolean ?: false get() = attributes[INHERITED_DESCRIPTOR_ATTRIBUTE].boolean ?: false
set(value) = attributes { set(value) = attributes.set(INHERITED_DESCRIPTOR_ATTRIBUTE, value)
set(INHERITED_DESCRIPTOR_ATTRIBUTE, value)
}
public val ItemDescriptor.usesStyles: Boolean
public val MetaDescriptor.usesStyles: Boolean
get() = attributes[STYLE_DESCRIPTOR_ATTRIBUTE].boolean ?: true get() = attributes[STYLE_DESCRIPTOR_ATTRIBUTE].boolean ?: true
public var ItemDescriptorBuilder.usesStyles: Boolean public var MetaDescriptorBuilder.usesStyles: Boolean
get() = attributes[STYLE_DESCRIPTOR_ATTRIBUTE].boolean ?: true get() = attributes[STYLE_DESCRIPTOR_ATTRIBUTE].boolean ?: true
set(value) = attributes { set(value) = attributes.set(STYLE_DESCRIPTOR_ATTRIBUTE, value)
set(STYLE_DESCRIPTOR_ATTRIBUTE, value)
}
public val Vision.describedProperties: Meta public val Vision.describedProperties: Meta
get() = Meta { get() = Meta {
descriptor?.items?.forEach { (key, descriptor) -> descriptor?.children?.forEach { (key, descriptor) ->
key put getProperty(key, inherit = descriptor.inherited) this.setMeta(key.asName(), getProperty(key, inherit = descriptor.inherited))
} }
} }
public val ValueDescriptor.widget: Meta public val MetaDescriptor.widget: Meta
get() = attributes["widget"].node ?: Meta.EMPTY get() = attributes["widget"] ?: Meta.EMPTY
/** /**
* Extension property to access the "widget" key of [ValueDescriptor] * Extension property to access the "widget" key of [ValueDescriptor]
*/ */
public var ValueDescriptorBuilder.widget: Meta public var MetaDescriptorBuilder.widget: Meta
get() = attributes["widget"].node ?: Meta.EMPTY get() = attributes["widget"] ?: Meta.EMPTY
set(value) { set(value) {
attributes { attributes["widget"] = value
set("widget", value)
}
} }
public val ValueDescriptor.widgetType: String? public val MetaDescriptor.widgetType: String?
get() = attributes["widget.type"].string get() = attributes["widget.type"].string
/** /**
* Extension property to access the "widget.type" key of [ValueDescriptor] * Extension property to access the "widget.type" key of [ValueDescriptor]
*/ */
public var ValueDescriptorBuilder.widgetType: String? public var MetaDescriptorBuilder.widgetType: String?
get() = attributes["widget.type"].string get() = attributes["widget.type"].string
set(value) { set(value) {
attributes { attributes["widget.type"] = value?.asValue()
set("widget.type", value)
}
} }
/** /**
* If true, this item is hidden in property editor. Default is false * If true, this item is hidden in property editor. Default is false
*/ */
public val ItemDescriptor.hidden: Boolean public val MetaDescriptor.hidden: Boolean
get() = attributes["widget.hide"].boolean ?: false get() = attributes["widget.hide"].boolean ?: false
public fun ItemDescriptorBuilder.hide(): Unit = attributes { public fun MetaDescriptorBuilder.hide(): Unit = attributes.set("widget.hide", true)
set("widget.hide", true)
}
public inline fun <reified E : Enum<E>> NodeDescriptorBuilder.enum(
key: Name,
default: E?,
crossinline modifier: ValueDescriptorBuilder.() -> Unit = {},
): Unit = value(key) {
type(ValueType.STRING)
default?.let {
default(default)
}
allowedValues = enumValues<E>().map { it.asValue() }
modifier()
}

View File

@ -57,7 +57,7 @@ class HtmlTagTest {
div { div {
h2 { +"Properties" } h2 { +"Properties" }
ul { ul {
(vision as? VisionBase)?.meta?.items?.forEach { (vision as? VisionBase)?.meta()?.items?.forEach {
li { li {
a { +it.key.toString() } a { +it.key.toString() }
p { +it.value.toString() } p { +it.value.toString() }

View File

@ -92,7 +92,7 @@ public class VisionClient : AbstractPlugin() {
} }
logger.debug { "Got update $change for output with name $name" } logger.debug { "Got update $change for output with name $name" }
vision.update(change) vision.change(change)
} else { } else {
console.error("WebSocket message data is not a string") console.error("WebSocket message data is not a string")
} }

View File

@ -115,5 +115,5 @@ public class ApplicationSurrogate : App() {
} }
public fun Context.display(width: Double = 800.0, height: Double = 600.0, component: () -> UIComponent) { public fun Context.display(width: Double = 800.0, height: Double = 600.0, component: () -> UIComponent) {
plugins.fetch(FXPlugin).display(component(), width, height) fetch(FXPlugin).display(component(), width, height)
} }

View File

@ -9,8 +9,8 @@ import javafx.collections.FXCollections
import javafx.scene.control.ComboBox import javafx.scene.control.ComboBox
import javafx.util.StringConverter import javafx.util.StringConverter
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.descriptors.allowedValues
import space.kscience.dataforge.meta.get import space.kscience.dataforge.meta.get
import space.kscience.dataforge.meta.value
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.asName
import space.kscience.dataforge.values.Value import space.kscience.dataforge.values.Value
@ -56,7 +56,7 @@ public class ComboBoxValueChooser(public val values: Collection<Value>? = null)
override val name: Name = "combo".asName() override val name: Name = "combo".asName()
override fun invoke(meta: Meta): ValueChooser = override fun invoke(meta: Meta): ValueChooser =
ComboBoxValueChooser(meta["values"].value?.list) ComboBoxValueChooser(meta["values"]?.value?.list)
} }
} }

View File

@ -1,189 +0,0 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package space.kscience.visionforge.editor
import de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon
import de.jensd.fx.glyphs.fontawesome.FontAwesomeIconView
import javafx.scene.Node
import javafx.scene.control.*
import javafx.scene.control.cell.TextFieldTreeTableCell
import javafx.scene.layout.BorderPane
import javafx.scene.layout.HBox
import javafx.scene.layout.Priority
import javafx.scene.paint.Color
import javafx.scene.text.Text
import space.kscience.dataforge.context.Global
import space.kscience.dataforge.meta.Config
import space.kscience.dataforge.meta.descriptors.NodeDescriptor
import space.kscience.dataforge.names.NameToken
import space.kscience.visionforge.dfIconView
import tornadofx.*
/**
* A configuration editor fragment
*
* @author Alexander Nozik
*/
public class ConfigEditor(
public val rootNode: FXMetaNode<Config>,
public val allowNew: Boolean = true,
title: String = "Configuration editor"
) : Fragment(title = title, icon = dfIconView) {
//TODO replace parameters by properties
public constructor(config: Config, descriptor: NodeDescriptor?, title: String = "Configuration editor") :
this(FXMeta.root(config, descriptor = descriptor), title = title)
override val root: BorderPane = borderpane {
center = treetableview<FXMeta<Config>> {
root = TreeItem(rootNode)
root.isExpanded = true
sortMode = TreeSortMode.ALL_DESCENDANTS
columnResizePolicy = TreeTableView.CONSTRAINED_RESIZE_POLICY
populate {
when (val fxMeta = it.value) {
is FXMetaNode -> {
fxMeta.children
}
is FXMetaValue -> null
}
}
column("Name", FXMeta<Config>::name) {
setCellFactory {
object : TextFieldTreeTableCell<FXMeta<Config>, NameToken>() {
override fun updateItem(item: NameToken?, empty: Boolean) {
super.updateItem(item, empty)
contextMenu?.items?.removeIf { it.text == "Remove" }
if (!empty) {
if (treeTableRow.item != null) {
textFillProperty().bind(treeTableRow.item.hasValue.objectBinding {
if (it == true) {
Color.BLACK
} else {
Color.GRAY
}
})
if (treeTableRow.treeItem.value.parent != null && treeTableRow.treeItem.value.hasValue.get()) {
contextmenu {
item("Remove") {
action {
treeTableRow.item.remove()
}
}
}
}
}
}
}
}
}
}
column("Value") { param: TreeTableColumn.CellDataFeatures<FXMeta<Config>, FXMeta<Config>> ->
param.value.valueProperty()
}.setCellFactory {
ValueCell()
}
column("Description") { param: TreeTableColumn.CellDataFeatures<FXMeta<Config>, String> -> param.value.value.descriptionProperty }
.setCellFactory { param: TreeTableColumn<FXMeta<Config>, String> ->
val cell = TreeTableCell<FXMeta<Config>, String>()
val text = Text()
cell.graphic = text
cell.prefHeight = Control.USE_COMPUTED_SIZE
text.wrappingWidthProperty().bind(param.widthProperty())
text.textProperty().bind(cell.itemProperty())
cell
}
}
}
private fun showNodeDialog(): String? {
val dialog = TextInputDialog()
dialog.title = "Node name selection"
dialog.contentText = "Enter a name for new node: "
dialog.headerText = null
val result = dialog.showAndWait()
return result.orElse(null)
}
private fun showValueDialog(): String? {
val dialog = TextInputDialog()
dialog.title = "Value name selection"
dialog.contentText = "Enter a name for new value: "
dialog.headerText = null
val result = dialog.showAndWait()
return result.orElse(null)
}
private inner class ValueCell : TreeTableCell<FXMeta<Config>, FXMeta<Config>?>() {
public override fun updateItem(item: FXMeta<Config>?, empty: Boolean) {
if (!empty) {
if (item != null) {
when (item) {
is FXMetaValue<Config> -> {
text = null
val chooser = ValueChooser.build(
Global,
item.valueProperty,
item.descriptor
) {
item.set(it)
}
graphic = chooser.node
}
is FXMetaNode<Config> -> {
if (allowNew) {
text = null
graphic = HBox().apply {
val glyph: Node = FontAwesomeIconView(FontAwesomeIcon.PLUS_CIRCLE)
button("node", graphic = glyph) {
hgrow = Priority.ALWAYS
maxWidth = Double.POSITIVE_INFINITY
action {
showNodeDialog()?.let {
item.addNode(it)
}
}
}
button("value", graphic = FontAwesomeIconView(FontAwesomeIcon.PLUS_SQUARE)) {
hgrow = Priority.ALWAYS
maxWidth = Double.POSITIVE_INFINITY
action {
showValueDialog()?.let {
item.addValue(it)
}
}
}
}
} else {
text = ""
}
}
}
} else {
text = null
graphic = null
}
} else {
text = null
graphic = null
}
}
}
companion object {
/**
* The tag not to display node or value in configurator
*/
const val NO_CONFIGURATOR_TAG = "nocfg"
}
}

View File

@ -1,223 +0,0 @@
package space.kscience.visionforge.editor
import javafx.beans.binding.ListBinding
import javafx.beans.binding.ObjectBinding
import javafx.beans.property.SimpleObjectProperty
import javafx.beans.value.ObservableBooleanValue
import javafx.beans.value.ObservableStringValue
import javafx.collections.ObservableList
import space.kscience.dataforge.meta.*
import space.kscience.dataforge.meta.descriptors.ItemDescriptor
import space.kscience.dataforge.meta.descriptors.NodeDescriptor
import space.kscience.dataforge.meta.descriptors.ValueDescriptor
import space.kscience.dataforge.names.*
import space.kscience.dataforge.values.Null
import space.kscience.dataforge.values.Value
import tornadofx.*
/**
* A display for meta and descriptor
*/
sealed class FXMeta<M : TypedMeta<M>> : Comparable<FXMeta<*>> {
abstract val name: NameToken
abstract val parent: FXMetaNode<M>?
abstract val descriptionProperty: ObservableStringValue
abstract val descriptor: ItemDescriptor?
abstract val hasValue: ObservableBooleanValue
override fun compareTo(other: FXMeta<*>): Int {
return if (this.hasValue.get() == other.hasValue.get()) {
this.name.toString().compareTo(other.name.toString())
} else {
this.hasValue.get().compareTo(other.hasValue.get())
}
}
companion object {
fun <M : TypedMeta<M>> root(
node: M,
descriptor: NodeDescriptor? = null,
rootName: String = "root"
): FXMetaNode<M> =
FXMetaNode(NameToken(rootName), null, node, descriptor)
fun root(node: Meta, descriptor: NodeDescriptor? = null, rootName: String = "root"): FXMetaNode<SealedMeta> =
root(node.seal(), descriptor, rootName)
}
}
class FXMetaNode<M : TypedMeta<M>>(
override val name: NameToken,
override val parent: FXMetaNode<M>?,
nodeValue: M? = null,
descriptorValue: NodeDescriptor? = null
) : FXMeta<M>() {
/**
* A descriptor that could be manually set to the node
*/
private val innerDescriptorProperty = SimpleObjectProperty(descriptorValue)
/**
* Actual descriptor which holds value inferred from parrent
*/
val descriptorProperty = objectBinding(innerDescriptorProperty) {
value ?: parent?.descriptor?.nodes?.get(this@FXMetaNode.name.body)
}
override val descriptor: NodeDescriptor? by descriptorProperty
private val innerNodeProperty = SimpleObjectProperty(nodeValue)
val nodeProperty: ObjectBinding<M?> = objectBinding(innerNodeProperty) {
value ?: parent?.node?.get(this@FXMetaNode.name).node
}
val node: M? by nodeProperty
override val descriptionProperty = innerDescriptorProperty.stringBinding { it?.info ?: "" }
override val hasValue: ObservableBooleanValue = nodeProperty.booleanBinding { it != null }
private val filter: (FXMeta<M>) -> Boolean = { cfg ->
!(cfg.descriptor?.attributes?.get(ConfigEditor.NO_CONFIGURATOR_TAG)?.boolean ?: false)
}
val children = object : ListBinding<FXMeta<M>>() {
init {
bind(nodeProperty, descriptorProperty)
val listener: (Name, MetaItem?, MetaItem?) -> Unit = { name, _, _ ->
if (name.length == 1) invalidate()
}
(node as? Config)?.onChange(this, listener)
nodeProperty.addListener { _, oldValue, newValue ->
if (newValue == null) {
(oldValue as? Config)?.removeListener(this)
}
if (newValue is Config) {
newValue.onChange(this, listener)
}
}
}
override fun computeValue(): ObservableList<FXMeta<M>> {
val nodeKeys = node?.items?.keys?.toSet() ?: emptySet()
val descriptorKeys = descriptor?.items?.keys?.map { NameToken(it) } ?: emptyList()
val keys: Set<NameToken> = nodeKeys + descriptorKeys
val items = keys.map { token ->
val actualItem = node?.items?.get(token)
val actualDescriptor = descriptor?.items?.get(token.body)
if (actualItem is MetaItemNode || actualDescriptor is NodeDescriptor) {
FXMetaNode(token, this@FXMetaNode)
} else {
FXMetaValue(token, this@FXMetaNode)
}
}
return items.filter(filter).asObservable()
}
}
init {
if (parent != null) {
parent.descriptorProperty.onChange { descriptorProperty.invalidate() }
parent.nodeProperty.onChange { nodeProperty.invalidate() }
}
}
}
public class FXMetaValue<M : TypedMeta<M>>(
override val name: NameToken,
override val parent: FXMetaNode<M>
) : FXMeta<M>() {
public val descriptorProperty = parent.descriptorProperty.objectBinding {
it?.values?.get(name.body)
}
/**
* A descriptor that could be manually set to the node
*/
override val descriptor: ValueDescriptor? by descriptorProperty
//private val innerValueProperty = SimpleObjectProperty(value)
public val valueProperty = descriptorProperty.objectBinding { descriptor ->
parent.node?.get(name).value ?: descriptor?.default
}
override val hasValue: ObservableBooleanValue = parent.nodeProperty.booleanBinding { it?.get(name) != null }
public val value by valueProperty
override val descriptionProperty = descriptorProperty.stringBinding { it?.info ?: "" }
}
public fun <M : MutableMeta<M>> FXMetaNode<M>.remove(name: NameToken) {
node?.remove(name.asName())
children.invalidate()
}
private fun <M : MutableMeta<M>> M.createEmptyNode(token: NameToken, append: Boolean): M {
return if (append && token.hasIndex()) {
val name = token.asName()
val index = (getIndexed(name).keys.mapNotNull { it?.toIntOrNull() }.maxOrNull() ?: -1) + 1
val newName = name.withIndex(index.toString())
set(newName, Meta.EMPTY)
get(newName).node!!
} else {
this.set(token.asName(), Meta.EMPTY)
//FIXME possible concurrency bug
get(token).node!!
}
}
fun <M : MutableMeta<M>> FXMetaNode<out M>.getOrCreateNode(): M {
val node = node
return when {
node != null -> node
parent != null -> parent.getOrCreateNode().createEmptyNode(this.name, descriptor?.multiple == true).also {
parent.children.invalidate()
}
else -> kotlin.error("Orphan empty node is not allowed")
}
}
fun <M : MutableMeta<M>> FXMeta<M>.remove() {
parent?.node?.remove(name.asName())
}
fun <M : MutableMeta<M>> FXMetaNode<M>.addValue(key: String) {
val parent = getOrCreateNode()
if (descriptor?.multiple == true) {
parent.append(key, Null)
} else {
parent[key] = Null
}
}
fun <M : MutableMeta<M>> FXMetaNode<M>.addNode(key: String) {
val parent = getOrCreateNode()
if (descriptor?.multiple == true) {
parent.append(key, Meta.EMPTY)
} else {
parent[key] = Meta.EMPTY
}
}
fun <M : MutableMeta<M>> FXMetaValue<M>.set(value: Value?) {
if (descriptor?.multiple == true) {
parent.getOrCreateNode().append(this.name.body, value)
} else {
parent.getOrCreateNode()[this.name] = value
}
}

View File

@ -0,0 +1,217 @@
package space.kscience.visionforge.editor
import javafx.beans.binding.Binding
import javafx.beans.binding.BooleanBinding
import javafx.beans.binding.ListBinding
import javafx.beans.property.SimpleObjectProperty
import javafx.collections.ObservableList
import space.kscience.dataforge.meta.*
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.meta.descriptors.get
import space.kscience.dataforge.names.*
import space.kscience.dataforge.values.Value
import tornadofx.*
/**
* A display for meta and descriptor
*/
public class FXMetaModel<M : Meta>(
public val root: M,
public val rootDescriptor: MetaDescriptor?,
public val nodeName: Name,
public val title: String = nodeName.lastOrNull()?.toString() ?: "Meta"
) : Comparable<FXMetaModel<*>> {
private val existingNode = SimpleObjectProperty<Meta>(root[nodeName])
public val children: ListBinding<FXMetaModel<M>> = object : ListBinding<FXMetaModel<M>>() {
override fun computeValue(): ObservableList<FXMetaModel<M>> {
val nodeKeys = existingNode.get().items.keys
val descriptorKeys = descriptor?.children?.keys?.map { NameToken(it) } ?: emptySet()
return (nodeKeys + descriptorKeys).map {
FXMetaModel(
root,
rootDescriptor,
nodeName + it
)
}.filter(filter).asObservable()
}
}
init {
//add listener to the root node if possible
if (root is ObservableMeta) {
root.onChange(this) { changed ->
if (changed.startsWith(nodeName)) {
if (nodeName.length == changed.length) existingNode.set(root[nodeName])
else if (changed.length == nodeName.length + 1) children.invalidate()
}
}
}
}
public val descriptor: MetaDescriptor? = rootDescriptor?.get(nodeName)
public val existsProperty: BooleanBinding = existingNode.isNotNull
public val exists: Boolean by existsProperty
public val valueProperty: Binding<Value?> = existingNode.objectBinding {
existingNode.get().value ?: descriptor?.defaultValue
}
override fun compareTo(other: FXMetaModel<*>): Int = if (this.exists == other.exists) {
this.nodeName.toString().compareTo(other.nodeName.toString())
} else {
this.exists.compareTo(other.exists)
}
public companion object {
private val filter: (FXMetaModel<*>) -> Boolean = { cfg ->
!(cfg.descriptor?.attributes?.get(MutableMetaEditor.NO_CONFIGURATOR_TAG)?.boolean ?: false)
}
public fun <M : Meta> root(
node: M,
descriptor: MetaDescriptor? = null,
rootName: String = "root"
): FXMetaModel<M> = FXMetaModel(node, descriptor, Name.EMPTY, title = rootName)
}
// /**
// * A descriptor that could be manually set to the node
// */
// private val innerDescriptorProperty = SimpleObjectProperty(descriptorValue)
//
// /**
// * Actual descriptor which holds value inferred from parrent
// */
// val descriptorProperty: ObjectBinding<MetaDescriptor?> = objectBinding(innerDescriptorProperty) {
// value ?: parent?.descriptor?.get(this@FXMeta.name.body)
// }
//
// val descriptor: MetaDescriptor? by descriptorProperty
//
// private val innerNodeProperty = SimpleObjectProperty(nodeValue)
//
// val nodeProperty: ObjectBinding<M> = objectBinding(innerNodeProperty) {
// value ?: parent?.node?.get(this@FXMeta.name)
// }
//
// val node by nodeProperty
//
// val hasValue: ObservableBooleanValue = nodeProperty.booleanBinding { it != null }
//
// private val filter: (FXMeta<M>) -> Boolean = { cfg ->
// !(cfg.descriptor?.attributes?.get(MutableMetaEditor.NO_CONFIGURATOR_TAG)?.boolean ?: false)
// }
//
// val children: ListBinding<FXMeta<M>> = object : ListBinding<FXMeta<M>>() {
//
// init {
// bind(nodeProperty, descriptorProperty)
//
// val listener: Meta.(Name) -> Unit = { name ->
// if (name.length == 1) invalidate()
// }
//
// (node as? ObservableMeta)?.onChange(this, listener)
//
// nodeProperty.addListener { _, oldValue, newValue ->
// if (newValue == null) {
// (oldValue as? ObservableMeta)?.removeListener(this)
// }
//
// if (newValue is ObservableMeta) {
// newValue.onChange(this, listener)
// }
// }
// }
//
// override fun computeValue(): ObservableList<FXMeta<M>> {
// val nodeKeys = node?.items?.keys?.toSet() ?: emptySet()
// val descriptorKeys = descriptor?.children?.keys?.map { NameToken(it) } ?: emptyList()
// val keys: Set<NameToken> = nodeKeys + descriptorKeys
//
// val items = keys.map { token ->
// val actualItem = node?.items?.get(token)
// val actualDescriptor = descriptor?.children?.get(token.body)
//
// if (actualItem is MetaNode) {
// FXMetaNode(token, this@FXMetaNode)
// } else {
// FXMetaValue(token, this@FXMetaNode)
// }
// }
//
// return items.filter(filter).asObservable()
// }
// }
//
// init {
// if (parent != null) {
// parent.descriptorProperty.onChange { descriptorProperty.invalidate() }
// parent.nodeProperty.onChange { nodeProperty.invalidate() }
// }
// }
//
}
//
//internal fun <M : MutableMeta> FXMeta<M>.remove(name: NameToken) {
// node?.remove(name.asName())
// children.invalidate()
//}
//
//private fun <M : MutableMeta> M.createEmptyNode(token: NameToken, append: Boolean): M {
// return if (append && token.hasIndex()) {
// val name = token.asName()
// val index = (getIndexed(name).keys.mapNotNull { it?.toIntOrNull() }.maxOrNull() ?: -1) + 1
// val newName = name.withIndex(index.toString())
// set(newName, Meta.EMPTY)
// get(newName).node
// } else {
// this.set(token.asName(), Meta.EMPTY)
// //FIXME possible concurrency bug
// get(token).node
// }
//}
//
//internal fun <M : MutableMeta> FXMeta<out M>.getOrCreateNode(): M {
// val node = node
// return when {
// node != null -> node
// parent != null -> parent.getOrCreateNode().createEmptyNode(this.name, descriptor?.multiple == true).also {
// parent.children.invalidate()
// }
// else -> kotlin.error("Orphan empty node is not allowed")
// }
//
//}
internal fun <M : MutableMeta> FXMetaModel<M>.remove() {
root.remove(nodeName)
}
//
//internal fun <M : MutableMeta> FXMeta<M>.addValue(key: String) {
// val parent = getOrCreateNode()
// if (descriptor?.multiple == true) {
// parent.append(key, Null)
// } else {
// parent[key] = Null
// }
//}
//
//internal fun <M : MutableMeta> FXMeta<M>.addNode(key: String) {
// val parent = getOrCreateNode()
// if (descriptor?.multiple == true) {
// parent.append(key, Meta.EMPTY)
// } else {
// parent[key] = Meta.EMPTY
// }
//}
//
internal fun <M : MutableMeta> FXMetaModel<M>.setValue(value: Value?) {
root.setValue(nodeName, value)
}

View File

@ -16,45 +16,42 @@
package space.kscience.visionforge.editor package space.kscience.visionforge.editor
import javafx.beans.property.SimpleStringProperty
import javafx.scene.control.TreeItem import javafx.scene.control.TreeItem
import javafx.scene.control.TreeSortMode import javafx.scene.control.TreeSortMode
import javafx.scene.control.TreeTableView import javafx.scene.control.TreeTableView
import javafx.scene.layout.BorderPane
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.values.string import space.kscience.dataforge.values.string
import space.kscience.visionforge.dfIconView import space.kscience.visionforge.dfIconView
import tornadofx.* import tornadofx.*
class MetaViewer(val rootNode: FXMetaNode<*>, title: String = "Meta viewer") : Fragment(title, public class MetaViewer(
dfIconView private val rootNode: FXMetaModel<Meta>,
) { title: String = "Meta viewer"
constructor(meta: Meta, title: String = "Meta viewer"): this( ) : Fragment(title, dfIconView) {
FXMeta.root(
meta
),title = title)
override val root = borderpane { public constructor(meta: Meta, title: String = "Meta viewer") : this(
FXMetaModel.root(
meta
), title = title
)
override val root: BorderPane = borderpane {
center { center {
treetableview<FXMeta<*>> { treetableview<FXMetaModel<*>> {
isShowRoot = false isShowRoot = false
root = TreeItem(rootNode) root = TreeItem(rootNode)
populate { populate {
when (val fxMeta = it.value) { val fxMeta = it.value
is FXMetaNode -> { fxMeta.children
fxMeta.children
}
is FXMetaValue -> null
}
} }
root.isExpanded = true root.isExpanded = true
sortMode = TreeSortMode.ALL_DESCENDANTS sortMode = TreeSortMode.ALL_DESCENDANTS
columnResizePolicy = TreeTableView.CONSTRAINED_RESIZE_POLICY columnResizePolicy = TreeTableView.CONSTRAINED_RESIZE_POLICY
column("Name", FXMeta<*>::name) column("Name", FXMetaModel<*>::title)
column<FXMeta<*>, String>("Value") { cellDataFeatures -> column<FXMetaModel<*>, String>("Value") { cellDataFeatures ->
when (val item = cellDataFeatures.value.value) { val item = cellDataFeatures.value.value
is FXMetaValue -> item.valueProperty.stringBinding { it?.string ?: "" } item.valueProperty.stringBinding { it?.string ?: "" }
is FXMetaNode -> SimpleStringProperty("[node]")
}
} }
} }
} }

View File

@ -0,0 +1,193 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package space.kscience.visionforge.editor
import javafx.scene.control.*
import javafx.scene.control.cell.TextFieldTreeTableCell
import javafx.scene.layout.BorderPane
import javafx.scene.paint.Color
import javafx.scene.text.Text
import space.kscience.dataforge.context.Global
import space.kscience.dataforge.meta.MutableMeta
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.visionforge.dfIconView
import tornadofx.*
/**
* A Configuration editor fragment
*
* @author Alexander Nozik
*/
public class MutableMetaEditor(
public val rootNode: FXMetaModel<MutableMeta>,
public val allowNew: Boolean = true,
title: String = "Configuration editor"
) : Fragment(title = title, icon = dfIconView) {
//TODO replace parameters by properties
public constructor(
MutableMeta: MutableMeta,
descriptor: MetaDescriptor?,
title: String = "Configuration editor"
) :
this(FXMetaModel.root(MutableMeta, descriptor = descriptor), title = title)
override val root: BorderPane = borderpane {
center = treetableview<FXMetaModel<MutableMeta>> {
root = TreeItem(rootNode)
root.isExpanded = true
sortMode = TreeSortMode.ALL_DESCENDANTS
columnResizePolicy = TreeTableView.CONSTRAINED_RESIZE_POLICY
populate {
it.value.children
}
column("Name", FXMetaModel<MutableMeta>::title) {
setCellFactory {
object : TextFieldTreeTableCell<FXMetaModel<MutableMeta>, String>() {
override fun updateItem(item: String?, empty: Boolean) {
super.updateItem(item, empty)
contextMenu?.items?.removeIf { it.text == "Remove" }
val content = treeTableRow.item
if (!empty) {
if (treeTableRow.item != null) {
textFillProperty().bind(content.existsProperty.objectBinding {
if (it == true) {
Color.BLACK
} else {
Color.GRAY
}
})
if (content.exists) {
contextmenu {
item("Remove") {
action {
content.remove()
}
}
}
}
}
}
}
}
}
}
column("Value") { param: TreeTableColumn.CellDataFeatures<FXMetaModel<MutableMeta>, FXMetaModel<MutableMeta>> ->
param.value.valueProperty()
}.setCellFactory {
ValueCell()
}
column("Description") { param: TreeTableColumn.CellDataFeatures<FXMetaModel<MutableMeta>, String> ->
(param.value.value.descriptor?.info ?: "").observable()
}.setCellFactory { param: TreeTableColumn<FXMetaModel<MutableMeta>, String> ->
val cell = TreeTableCell<FXMetaModel<MutableMeta>, String>()
val text = Text()
cell.graphic = text
cell.prefHeight = Control.USE_COMPUTED_SIZE
text.wrappingWidthProperty().bind(param.widthProperty())
text.textProperty().bind(cell.itemProperty())
cell
}
}
}
private fun showNodeDialog(): String? {
val dialog = TextInputDialog()
dialog.title = "Node name selection"
dialog.contentText = "Enter a name for new node: "
dialog.headerText = null
val result = dialog.showAndWait()
return result.orElse(null)
}
private fun showValueDialog(): String? {
val dialog = TextInputDialog()
dialog.title = "Value name selection"
dialog.contentText = "Enter a name for new value: "
dialog.headerText = null
val result = dialog.showAndWait()
return result.orElse(null)
}
private inner class ValueCell : TreeTableCell<FXMetaModel<MutableMeta>, FXMetaModel<MutableMeta>?>() {
public override fun updateItem(item: FXMetaModel<MutableMeta>?, empty: Boolean) {
if (!empty) {
if (item != null) {
text = null
val chooser = ValueChooser.build(
Global,
item.valueProperty,
item.descriptor
) {
item.setValue(it)
}
graphic = chooser.node
// when (item) {
// is FXMetaValue<MutableMeta> -> {
// text = null
// val chooser = ValueChooser.build(
// Global,
// item.valueProperty,
// item.descriptor
// ) {
// item.set(it)
// }
// graphic = chooser.node
// }
// is FXMetaNode<MutableMeta> -> {
// if (allowNew) {
// text = null
// graphic = HBox().apply {
// val glyph: Node = FontAwesomeIconView(FontAwesomeIcon.PLUS_CIRCLE)
// button("node", graphic = glyph) {
// hgrow = Priority.ALWAYS
// maxWidth = Double.POSITIVE_INFINITY
// action {
// showNodeDialog()?.let {
// item.addNode(it)
// }
// }
// }
// button("value", graphic = FontAwesomeIconView(FontAwesomeIcon.PLUS_SQUARE)) {
// hgrow = Priority.ALWAYS
// maxWidth = Double.POSITIVE_INFINITY
// action {
// showValueDialog()?.let {
// item.addValue(it)
// }
// }
// }
// }
// } else {
// text = ""
// }
// }
// }
} else {
text = null
graphic = null
}
} else {
text = null
graphic = null
}
}
}
public companion object {
/**
* The tag not to display node or value in MutableMetaurator
*/
public const val NO_CONFIGURATOR_TAG: String = "nocfg"
}
}

View File

@ -10,12 +10,13 @@ import javafx.scene.control.TextField
import javafx.scene.input.KeyCode import javafx.scene.input.KeyCode
import javafx.scene.input.KeyEvent import javafx.scene.input.KeyEvent
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.descriptors.validate
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.asName
import space.kscience.dataforge.values.* import space.kscience.dataforge.values.*
import tornadofx.* import tornadofx.*
class TextValueChooser : ValueChooserBase<TextField>() { public class TextValueChooser : ValueChooserBase<TextField>() {
private val displayText: String private val displayText: String
get() = currentValue().let { get() = currentValue().let {
@ -85,7 +86,7 @@ class TextValueChooser : ValueChooserBase<TextField>() {
} }
private fun validate(value: Value): Boolean { private fun validate(value: Value): Boolean {
return descriptor?.isAllowedValue(value) ?: true return descriptor?.validate(value) ?: true
} }
// @Override // @Override
@ -101,7 +102,7 @@ class TextValueChooser : ValueChooserBase<TextField>() {
} }
} }
companion object : ValueChooser.Factory { public companion object : ValueChooser.Factory {
override val name: Name = "text".asName() override val name: Name = "text".asName()
override fun invoke(meta: Meta): ValueChooser = override fun invoke(meta: Meta): ValueChooser =
TextValueChooser() TextValueChooser()

View File

@ -13,11 +13,11 @@ import space.kscience.dataforge.values.Value
* @param value Value after change * @param value Value after change
* @param message Message on unsuccessful change * @param message Message on unsuccessful change
*/ */
class ValueCallbackResponse(val success: Boolean, val value: Value, val message: String) public class ValueCallbackResponse(public val success: Boolean, public val value: Value, public val message: String)
/** /**
* A callback for some visual object trying to change some value * A callback for some visual object trying to change some value
* @author [Alexander Nozik](mailto:altavir@gmail.com) * @author [Alexander Nozik](mailto:altavir@gmail.com)
*/ */
typealias ValueCallback = (Value) -> ValueCallbackResponse public typealias ValueCallback = (Value) -> ValueCallbackResponse

View File

@ -10,10 +10,12 @@ import javafx.beans.value.ObservableValue
import javafx.scene.Node import javafx.scene.Node
import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Context
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.descriptors.ValueDescriptor import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.meta.descriptors.allowedValues
import space.kscience.dataforge.meta.descriptors.validate
import space.kscience.dataforge.misc.Named import space.kscience.dataforge.misc.Named
import space.kscience.dataforge.misc.Type import space.kscience.dataforge.misc.Type
import space.kscience.dataforge.names.toName import space.kscience.dataforge.names.Name
import space.kscience.dataforge.provider.provideByType import space.kscience.dataforge.provider.provideByType
import space.kscience.dataforge.values.Null import space.kscience.dataforge.values.Null
import space.kscience.dataforge.values.Value import space.kscience.dataforge.values.Value
@ -42,8 +44,8 @@ public interface ValueChooser {
* *
* @return * @return
*/ */
public val descriptorProperty: ObjectProperty<ValueDescriptor?> public val descriptorProperty: ObjectProperty<MetaDescriptor?>
public var descriptor: ValueDescriptor? public var descriptor: MetaDescriptor?
public val valueProperty: ObjectProperty<Value?> public val valueProperty: ObjectProperty<Value?>
public var value: Value? public var value: Value?
@ -71,7 +73,7 @@ public interface ValueChooser {
public companion object { public companion object {
private fun findWidgetByType(context: Context, type: String): Factory? { private fun findWidgetByType(context: Context, type: String): Factory? {
return when (type.toName()) { return when (Name.parse(type)) {
TextValueChooser.name -> TextValueChooser TextValueChooser.name -> TextValueChooser
ColorValueChooser.name -> ColorValueChooser ColorValueChooser.name -> ColorValueChooser
ComboBoxValueChooser.name -> ComboBoxValueChooser ComboBoxValueChooser.name -> ComboBoxValueChooser
@ -79,7 +81,7 @@ public interface ValueChooser {
} }
} }
private fun build(context: Context, descriptor: ValueDescriptor?): ValueChooser { private fun build(context: Context, descriptor: MetaDescriptor?): ValueChooser {
return if (descriptor == null) { return if (descriptor == null) {
TextValueChooser(); TextValueChooser();
} else { } else {
@ -93,7 +95,7 @@ public interface ValueChooser {
descriptor.widget descriptor.widget
) ?: TextValueChooser() ) ?: TextValueChooser()
} }
descriptor.allowedValues.isNotEmpty() -> ComboBoxValueChooser() !descriptor.allowedValues.isNullOrEmpty() -> ComboBoxValueChooser()
else -> TextValueChooser() else -> TextValueChooser()
} }
chooser.descriptor = descriptor chooser.descriptor = descriptor
@ -101,10 +103,10 @@ public interface ValueChooser {
} }
} }
fun build( public fun build(
context: Context, context: Context,
value: ObservableValue<Value?>, value: ObservableValue<Value?>,
descriptor: ValueDescriptor? = null, descriptor: MetaDescriptor? = null,
setter: (Value) -> Unit, setter: (Value) -> Unit,
): ValueChooser { ): ValueChooser {
val chooser = build(context, descriptor) val chooser = build(context, descriptor)
@ -113,7 +115,7 @@ public interface ValueChooser {
chooser.setDisplayValue(it ?: Null) chooser.setDisplayValue(it ?: Null)
} }
chooser.setCallback { result -> chooser.setCallback { result ->
if (descriptor?.isAllowedValue(result) != false) { if (descriptor?.validate(result) != false) {
setter(result) setter(result)
ValueCallbackResponse(true, result, "OK") ValueCallbackResponse(true, result, "OK")
} else { } else {

View File

@ -8,7 +8,7 @@ package space.kscience.visionforge.editor
import javafx.beans.property.SimpleObjectProperty import javafx.beans.property.SimpleObjectProperty
import javafx.scene.Node import javafx.scene.Node
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import space.kscience.dataforge.meta.descriptors.ValueDescriptor import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.values.Null import space.kscience.dataforge.values.Null
import space.kscience.dataforge.values.Value import space.kscience.dataforge.values.Value
import tornadofx.* import tornadofx.*
@ -18,16 +18,16 @@ import tornadofx.*
* *
* @author Alexander Nozik * @author Alexander Nozik
*/ */
abstract class ValueChooserBase<out T : Node> : ValueChooser { public abstract class ValueChooserBase<out T : Node> : ValueChooser {
override val node by lazy { buildNode() } override val node: T by lazy { buildNode() }
final override val valueProperty = SimpleObjectProperty<Value>(Null) final override val valueProperty: SimpleObjectProperty<Value> = SimpleObjectProperty<Value>(Null)
final override val descriptorProperty = SimpleObjectProperty<ValueDescriptor>() final override val descriptorProperty: SimpleObjectProperty<MetaDescriptor> = SimpleObjectProperty<MetaDescriptor>()
override var descriptor: ValueDescriptor? by descriptorProperty override var descriptor: MetaDescriptor? by descriptorProperty
override var value: Value? by valueProperty override var value: Value? by valueProperty
fun resetValue() { public fun resetValue() {
setDisplayValue(currentValue()) setDisplayValue(currentValue())
} }
@ -36,7 +36,7 @@ abstract class ValueChooserBase<out T : Node> : ValueChooser {
* @return * @return
*/ */
protected fun currentValue(): Value { protected fun currentValue(): Value {
return value ?: descriptor?.default ?: Null return value ?: descriptor?.defaultValue ?: Null
} }
/** /**

View File

@ -5,39 +5,36 @@ import javafx.beans.property.SimpleObjectProperty
import javafx.scene.Node import javafx.scene.Node
import javafx.scene.Parent import javafx.scene.Parent
import javafx.scene.layout.VBox import javafx.scene.layout.VBox
import space.kscience.dataforge.meta.Config import space.kscience.dataforge.meta.*
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.meta.MutableItemProvider
import space.kscience.dataforge.meta.descriptors.NodeDescriptor
import space.kscience.dataforge.meta.update
import space.kscience.visionforge.* import space.kscience.visionforge.*
import tornadofx.* import tornadofx.*
class VisualObjectEditorFragment(val selector: (Vision) -> Meta) : Fragment() { public class VisionEditorFragment(public val selector: (Vision) -> Meta) : Fragment() {
val itemProperty = SimpleObjectProperty<Vision>() public val itemProperty: SimpleObjectProperty<Vision> = SimpleObjectProperty<Vision>()
var item: Vision? by itemProperty public var item: Vision? by itemProperty
val descriptorProperty = SimpleObjectProperty<NodeDescriptor>() public val descriptorProperty: SimpleObjectProperty<MetaDescriptor> = SimpleObjectProperty<MetaDescriptor>()
constructor( public constructor(
item: Vision?, item: Vision?,
descriptor: NodeDescriptor?, descriptor: MetaDescriptor?,
selector: (Vision) -> MutableItemProvider = { it.allProperties() }, selector: (Vision) -> MutableMetaProvider = { it.meta() },
) : this({ it.describedProperties }) { ) : this({ it.describedProperties }) {
this.item = item this.item = item
this.descriptorProperty.set(descriptor) this.descriptorProperty.set(descriptor)
} }
private var currentConfig: Config? = null private var currentConfig: ObservableMutableMeta? = null
private val configProperty: Binding<Config?> = itemProperty.objectBinding { visualObject -> private val configProperty: Binding<ObservableMutableMeta?> = itemProperty.objectBinding { vision ->
if (visualObject == null) return@objectBinding null if (vision == null) return@objectBinding null
val meta = selector(visualObject) val meta = selector(vision)
val config = Config().apply { val config = MutableMeta {
update(meta) update(meta)
onChange(this@VisualObjectEditorFragment) { key, _, after -> }
visualObject.setProperty(key, after) config.onChange(this@VisionEditorFragment) { key ->
} vision.setPropertyNode(key, config[key])
} }
//remember old config reference to cleanup listeners //remember old config reference to cleanup listeners
currentConfig?.removeListener(this) currentConfig?.removeListener(this)
@ -47,7 +44,7 @@ class VisualObjectEditorFragment(val selector: (Vision) -> Meta) : Fragment() {
private val configEditorProperty: Binding<Node?> = configProperty.objectBinding(descriptorProperty) { private val configEditorProperty: Binding<Node?> = configProperty.objectBinding(descriptorProperty) {
it?.let { it?.let {
ConfigEditor(it, descriptorProperty.get()).root MutableMetaEditor(it, descriptorProperty.get()).root
} }
} }

View File

@ -3,6 +3,7 @@ package space.kscience.visionforge.editor
import javafx.beans.property.SimpleObjectProperty import javafx.beans.property.SimpleObjectProperty
import javafx.scene.control.SelectionMode import javafx.scene.control.SelectionMode
import javafx.scene.control.TreeItem import javafx.scene.control.TreeItem
import javafx.scene.layout.VBox
import space.kscience.visionforge.Vision import space.kscience.visionforge.Vision
import space.kscience.visionforge.VisionGroup import space.kscience.visionforge.VisionGroup
import tornadofx.* import tornadofx.*
@ -29,13 +30,13 @@ private fun toTreeItem(vision: Vision, title: String): TreeItem<Pair<String, Vis
} }
class VisualObjectTreeFragment : Fragment() { public class VisionTreeFragment : Fragment() {
val itemProperty = SimpleObjectProperty<Vision>() public val itemProperty: SimpleObjectProperty<Vision> = SimpleObjectProperty<Vision>()
var item: Vision? by itemProperty public var item: Vision? by itemProperty
val selectedProperty = SimpleObjectProperty<Vision>() public val selectedProperty: SimpleObjectProperty<Vision> = SimpleObjectProperty<Vision>()
override val root = vbox { override val root: VBox = vbox {
titledpane("Object tree", collapsible = false) { titledpane("Object tree", collapsible = false) {
treeview<Pair<String, Vision>> { treeview<Pair<String, Vision>> {
cellFormat { cellFormat {

View File

@ -3,11 +3,15 @@ package space.kscience.visionforge.solid
import javafx.scene.paint.Color import javafx.scene.paint.Color
import javafx.scene.paint.Material import javafx.scene.paint.Material
import javafx.scene.paint.PhongMaterial import javafx.scene.paint.PhongMaterial
import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.double
import space.kscience.dataforge.meta.get
import space.kscience.dataforge.meta.int
import space.kscience.dataforge.values.ValueType import space.kscience.dataforge.values.ValueType
import space.kscience.dataforge.values.int import space.kscience.dataforge.values.int
import space.kscience.dataforge.values.string import space.kscience.dataforge.values.string
import space.kscience.visionforge.Colors import space.kscience.visionforge.Colors
import space.kscience.visionforge.solid.FXMaterials.GREY
public object FXMaterials { public object FXMaterials {
public val RED: PhongMaterial = PhongMaterial().apply { public val RED: PhongMaterial = PhongMaterial().apply {
@ -26,46 +30,43 @@ public object FXMaterials {
} }
public val BLUE: PhongMaterial = PhongMaterial(Color.BLUE) public val BLUE: PhongMaterial = PhongMaterial(Color.BLUE)
} }
/** /**
* Infer color based on meta item * Infer color based on meta item
* @param opacity default opacity * @param opacity default opacity
*/ */
public fun MetaItem.color(opacity: Double = 1.0): Color { public fun Meta.color(opacity: Double = 1.0): Color {
return when (this) { return value?.let {
is MetaItemValue -> if (this.value.type == ValueType.NUMBER) { if (it.type == ValueType.NUMBER) {
val int = value.int val int = it.int
val red = int and 0x00ff0000 shr 16 val red = int and 0x00ff0000 shr 16
val green = int and 0x0000ff00 shr 8 val green = int and 0x0000ff00 shr 8
val blue = int and 0x000000ff val blue = int and 0x000000ff
Color.rgb(red, green, blue, opacity) Color.rgb(red, green, blue, opacity)
} else { } else {
Color.web(this.value.string) Color.web(it.string)
} }
is MetaItemNode -> { } ?: Color.rgb(
Color.rgb( this[Colors.RED_KEY]?.int ?: 0,
node[Colors.RED_KEY]?.int ?: 0, this[Colors.GREEN_KEY]?.int ?: 0,
node[Colors.GREEN_KEY]?.int ?: 0, this[Colors.BLUE_KEY]?.int ?: 0,
node[Colors.BLUE_KEY]?.int ?: 0, this[SolidMaterial.OPACITY_KEY]?.double ?: opacity
node[SolidMaterial.OPACITY_KEY]?.double ?: opacity )
)
}
}
} }
/** /**
* Infer FX material based on meta item * Infer FX material based on meta item
*/ */
public fun MetaItem?.material(): Material { public fun Meta?.material(): Material {
return when (this) { if (this == null) return GREY
null -> FXMaterials.GREY return value?.let {
is MetaItemValue -> PhongMaterial(color()) PhongMaterial(color())
is MetaItemNode -> PhongMaterial().apply { } ?: PhongMaterial().apply {
val opacity = node[SolidMaterial.OPACITY_KEY].double ?: 1.0 val opacity = get(SolidMaterial.OPACITY_KEY).double ?: 1.0
diffuseColor = node[SolidMaterial.COLOR_KEY]?.color(opacity) ?: Color.DARKGREY diffuseColor = get(SolidMaterial.COLOR_KEY)?.color(opacity) ?: Color.DARKGREY
specularColor = node[SolidMaterial.SPECULAR_COLOR_KEY]?.color(opacity) ?: Color.WHITE specularColor = get(SolidMaterial.SPECULAR_COLOR_KEY)?.color(opacity) ?: Color.WHITE
}
} }
} }

View File

@ -2,7 +2,10 @@ package space.kscience.visionforge.solid
import javafx.scene.Group import javafx.scene.Group
import javafx.scene.Node import javafx.scene.Node
import space.kscience.dataforge.names.* import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.cutFirst
import space.kscience.dataforge.names.firstOrNull
import space.kscience.dataforge.names.isEmpty
import space.kscience.visionforge.Vision import space.kscience.visionforge.Vision
import space.kscience.visionforge.onPropertyChange import space.kscience.visionforge.onPropertyChange
import kotlin.reflect.KClass import kotlin.reflect.KClass
@ -16,7 +19,7 @@ class FXReferenceFactory(val plugin: FX3DPlugin) : FX3DFactory<SolidReferenceGro
obj.onPropertyChange(plugin.context) { name-> obj.onPropertyChange(plugin.context) { name->
if (name.firstOrNull()?.body == SolidReferenceGroup.REFERENCE_CHILD_PROPERTY_PREFIX) { if (name.firstOrNull()?.body == SolidReferenceGroup.REFERENCE_CHILD_PROPERTY_PREFIX) {
val childName = name.firstOrNull()?.index?.toName() ?: error("Wrong syntax for reference child property: '$name'") val childName = name.firstOrNull()?.index?.let(Name::parse) ?: error("Wrong syntax for reference child property: '$name'")
val propertyName = name.cutFirst() val propertyName = name.cutFirst()
val referenceChild = obj[childName] ?: error("Reference child with name '$childName' not found") val referenceChild = obj[childName] ?: error("Reference child with name '$childName' not found")
val child = node.findChild(childName) ?: error("Object child with name '$childName' not found") val child = node.findChild(childName) ?: error("Object child with name '$childName' not found")

View File

@ -5,7 +5,6 @@ import javafx.beans.binding.*
import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.*
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.startsWith import space.kscience.dataforge.names.startsWith
import space.kscience.dataforge.names.toName
import space.kscience.dataforge.values.Value import space.kscience.dataforge.values.Value
import space.kscience.visionforge.Vision import space.kscience.visionforge.Vision
import space.kscience.visionforge.onPropertyChange import space.kscience.visionforge.onPropertyChange
@ -15,7 +14,7 @@ import tornadofx.*
* A caching binding collection for [Vision] properties * A caching binding collection for [Vision] properties
*/ */
public class VisualObjectFXBinding(public val fx: FX3DPlugin, public val obj: Vision) { public class VisualObjectFXBinding(public val fx: FX3DPlugin, public val obj: Vision) {
private val bindings = HashMap<Name, ObjectBinding<MetaItem?>>() private val bindings = HashMap<Name, ObjectBinding<Meta?>>()
init { init {
obj.onPropertyChange(fx.context) { name -> obj.onPropertyChange(fx.context) { name ->
@ -33,30 +32,29 @@ public class VisualObjectFXBinding(public val fx: FX3DPlugin, public val obj: Vi
} }
} }
public operator fun get(key: Name): ObjectBinding<MetaItem?> { public operator fun get(key: Name): ObjectBinding<Meta?> {
return bindings.getOrPut(key) { return bindings.getOrPut(key) {
object : ObjectBinding<MetaItem?>() { object : ObjectBinding<Meta?>() {
override fun computeValue(): MetaItem? = obj.getProperty(key) override fun computeValue(): Meta? = obj.getProperty(key)
} }
} }
} }
public operator fun get(key: String) = get(key.toName()) public operator fun get(key: String): ObjectBinding<Meta?> = get(Name.parse(key))
} }
public fun ObjectBinding<MetaItem?>.value(): Binding<Value?> = objectBinding { it.value } public fun ObjectBinding<Meta?>.value(): Binding<Value?> = objectBinding { it?.value }
public fun ObjectBinding<MetaItem?>.string(): StringBinding = stringBinding { it.string } public fun ObjectBinding<Meta?>.string(): StringBinding = stringBinding { it.string }
public fun ObjectBinding<MetaItem?>.number(): Binding<Number?> = objectBinding { it.number } public fun ObjectBinding<Meta?>.number(): Binding<Number?> = objectBinding { it.number }
public fun ObjectBinding<MetaItem?>.double(): Binding<Double?> = objectBinding { it.double } public fun ObjectBinding<Meta?>.double(): Binding<Double?> = objectBinding { it.double }
public fun ObjectBinding<MetaItem?>.float(): Binding<Float?> = objectBinding { it.float } public fun ObjectBinding<Meta?>.float(): Binding<Float?> = objectBinding { it.float }
public fun ObjectBinding<MetaItem?>.int(): Binding<Int?> = objectBinding { it.int } public fun ObjectBinding<Meta?>.int(): Binding<Int?> = objectBinding { it.int }
public fun ObjectBinding<MetaItem?>.long(): Binding<Long?> = objectBinding { it.long } public fun ObjectBinding<Meta?>.long(): Binding<Long?> = objectBinding { it.long }
public fun ObjectBinding<MetaItem?>.node(): Binding<Meta?> = objectBinding { it.node }
public fun ObjectBinding<MetaItem?>.string(default: String): StringBinding = stringBinding { it.string ?: default } public fun ObjectBinding<Meta?>.string(default: String): StringBinding = stringBinding { it.string ?: default }
public fun ObjectBinding<MetaItem?>.double(default: Double): DoubleBinding = doubleBinding { it.double ?: default } public fun ObjectBinding<Meta?>.double(default: Double): DoubleBinding = doubleBinding { it.double ?: default }
public fun ObjectBinding<MetaItem?>.float(default: Float): FloatBinding = floatBinding { it.float ?: default } public fun ObjectBinding<Meta?>.float(default: Float): FloatBinding = floatBinding { it.float ?: default }
public fun ObjectBinding<MetaItem?>.int(default: Int): IntegerBinding = integerBinding { it.int ?: default } public fun ObjectBinding<Meta?>.int(default: Int): IntegerBinding = integerBinding { it.int ?: default }
public fun ObjectBinding<MetaItem?>.long(default: Long): LongBinding = longBinding { it.long ?: default } public fun ObjectBinding<Meta?>.long(default: Long): LongBinding = longBinding { it.long ?: default }
public fun <T> ObjectBinding<MetaItem?>.transform(transform: (MetaItem) -> T): Binding<T?> = objectBinding { it?.let(transform) } public fun <T> ObjectBinding<Meta?>.transform(transform: (Meta) -> T): Binding<T?> = objectBinding { it?.let(transform) }

View File

@ -1,12 +1,12 @@
package space.kscience.visionforge.gdml package space.kscience.visionforge.gdml
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.MetaBuilder import space.kscience.dataforge.meta.MutableMeta
import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.asName
import space.kscience.dataforge.names.plus import space.kscience.dataforge.names.plus
import space.kscience.dataforge.names.toName
import space.kscience.gdml.* import space.kscience.gdml.*
import space.kscience.visionforge.* import space.kscience.visionforge.*
import space.kscience.visionforge.html.VisionOutput import space.kscience.visionforge.html.VisionOutput
@ -41,8 +41,8 @@ public class GdmlTransformer {
internal val styleCache = HashMap<Name, Meta>() internal val styleCache = HashMap<Name, Meta>()
public fun Solid.registerAndUseStyle(name: String, builder: MetaBuilder.() -> Unit) { public fun Solid.registerAndUseStyle(name: String, builder: MutableMeta.() -> Unit) {
styleCache.getOrPut(name.toName()) { styleCache.getOrPut(Name.parse(name)) {
Meta(builder) Meta(builder)
} }
useStyle(name) useStyle(name)
@ -118,7 +118,7 @@ private class GdmlTransformerEnv(val settings: GdmlTransformer) {
private val proto = SolidGroup() private val proto = SolidGroup()
private val solids = proto.group(solidsName) { private val solids = proto.group(solidsName) {
setProperty("edges.enabled", false) setPropertyNode("edges.enabled", false)
} }
private val referenceStore = HashMap<Name, MutableList<SolidReferenceGroup>>() private val referenceStore = HashMap<Name, MutableList<SolidReferenceGroup>>()

View File

@ -1,7 +1,7 @@
package space.kscience.visionforge.gdml package space.kscience.visionforge.gdml
import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Context
import space.kscience.dataforge.names.toName import space.kscience.dataforge.names.Name
import space.kscience.gdml.* import space.kscience.gdml.*
import space.kscience.visionforge.Vision import space.kscience.visionforge.Vision
import space.kscience.visionforge.get import space.kscience.visionforge.get
@ -23,7 +23,7 @@ class TestCubes {
fun testCubesDirect() { fun testCubesDirect() {
val vision = cubes.toVision() val vision = cubes.toVision()
// println(Solids.encodeToString(vision)) // println(Solids.encodeToString(vision))
val smallBoxPrototype = vision.getPrototype("solids.smallBox".toName()) as? Box val smallBoxPrototype = vision.getPrototype(Name.parse("solids.smallBox")) as? Box
assertNotNull(smallBoxPrototype) assertNotNull(smallBoxPrototype)
assertEquals(30.0, smallBoxPrototype.xSize.toDouble()) assertEquals(30.0, smallBoxPrototype.xSize.toDouble())
val smallBoxVision = vision["composite-111.smallBox"]?.unref as? Box val smallBoxVision = vision["composite-111.smallBox"]?.unref as? Box
@ -46,7 +46,7 @@ class TestCubes {
val vision = cubes.toVision() val vision = cubes.toVision()
val serialized = Solids.encodeToString(vision) val serialized = Solids.encodeToString(vision)
val deserialized = testContext.visionManager.decodeFromString(serialized) as SolidGroup val deserialized = testContext.visionManager.decodeFromString(serialized) as SolidGroup
val smallBox = deserialized.getPrototype("solids.smallBox".toName()) as? Box val smallBox = deserialized.getPrototype(Name.parse("solids.smallBox")) as? Box
assertNotNull(smallBox) assertNotNull(smallBox)
assertEquals(30.0, smallBox.xSize.toDouble()) assertEquals(30.0, smallBox.xSize.toDouble())
//println(testContext.visionManager.encodeToString(deserialized)) //println(testContext.visionManager.encodeToString(deserialized))

View File

@ -1,7 +1,7 @@
package space.kscience.visionforge.gdml package space.kscience.visionforge.gdml
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import space.kscience.dataforge.names.toName import space.kscience.dataforge.names.Name
import space.kscience.gdml.Gdml import space.kscience.gdml.Gdml
import space.kscience.gdml.decodeFromStream import space.kscience.gdml.decodeFromStream
import space.kscience.visionforge.solid.Solids import space.kscience.visionforge.solid.Solids
@ -23,7 +23,7 @@ class TestConvertor {
val stream = javaClass.getResourceAsStream("/gdml/cubes.gdml")!! val stream = javaClass.getResourceAsStream("/gdml/cubes.gdml")!!
val gdml = Gdml.decodeFromStream(stream) val gdml = Gdml.decodeFromStream(stream)
val vision = gdml.toVision() val vision = gdml.toVision()
assertNotNull(vision.getPrototype("solids.box".toName())) assertNotNull(vision.getPrototype(Name.parse("solids.box")))
println(Solids.encodeToString(vision)) println(Solids.encodeToString(vision))
} }

View File

@ -7,7 +7,7 @@ import kotlinx.serialization.modules.polymorphic
import kotlinx.serialization.modules.subclass import kotlinx.serialization.modules.subclass
import space.kscience.dataforge.meta.string import space.kscience.dataforge.meta.string
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.toName import space.kscience.dataforge.names.asName
import space.kscience.visionforge.Vision import space.kscience.visionforge.Vision
import space.kscience.visionforge.VisionBase import space.kscience.visionforge.VisionBase
import space.kscience.visionforge.setProperty import space.kscience.visionforge.setProperty
@ -27,7 +27,7 @@ public class VisionOfMarkup(
} }
public companion object { public companion object {
public val CONTENT_PROPERTY_KEY: Name = "content".toName() public val CONTENT_PROPERTY_KEY: Name = "content".asName()
public const val COMMONMARK_FORMAT: String = "markdown.commonmark" public const val COMMONMARK_FORMAT: String = "markdown.commonmark"
public const val GFM_FORMAT: String = "markdown.gfm" public const val GFM_FORMAT: String = "markdown.gfm"
} }

View File

@ -2,7 +2,7 @@ plugins {
id("ru.mipt.npm.gradle.mpp") id("ru.mipt.npm.gradle.mpp")
} }
val plotlyVersion = "0.4.3" val plotlyVersion = "0.5.0-dev-1"
kscience { kscience {
useSerialization() useSerialization()

View File

@ -2,6 +2,8 @@ package space.kscience.visionforge.plotly
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import space.kscience.dataforge.meta.MutableMeta
import space.kscience.dataforge.meta.asObservable
import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.misc.DFExperimental
import space.kscience.plotly.Plot import space.kscience.plotly.Plot
import space.kscience.plotly.Plotly import space.kscience.plotly.Plotly
@ -13,10 +15,10 @@ import space.kscience.visionforge.root
@SerialName("vision.plotly") @SerialName("vision.plotly")
public class VisionOfPlotly private constructor() : VisionBase() { public class VisionOfPlotly private constructor() : VisionBase() {
public constructor(plot: Plot) : this() { public constructor(plot: Plot) : this() {
properties = plot.config properties = plot.meta
} }
public val plot: Plot get() = Plot(properties ?: Config()) public val plot: Plot get() = Plot(properties?.asObservable() ?: MutableMeta())
} }
public fun Plot.asVision(): VisionOfPlotly = VisionOfPlotly(this) public fun Plot.asVision(): VisionOfPlotly = VisionOfPlotly(this)

View File

@ -31,7 +31,7 @@ public actual class PlotlyPlugin : VisionPlugin(), ElementVisionRenderer {
override fun render(element: Element, vision: Vision, meta: Meta) { override fun render(element: Element, vision: Vision, meta: Meta) {
val plot = (vision as? VisionOfPlotly)?.plot ?: error("VisionOfPlotly expected but ${vision::class} found") val plot = (vision as? VisionOfPlotly)?.plot ?: error("VisionOfPlotly expected but ${vision::class} found")
val config = PlotlyConfig.read(meta) val config = PlotlyConfig.read(meta)
println(plot.config) println(plot.meta)
println(plot.data[0].toMeta()) println(plot.data[0].toMeta())
element.plot(plot, config) element.plot(plot, config)
} }

View File

@ -30,7 +30,6 @@ import space.kscience.dataforge.context.fetch
import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.*
import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.toName
import space.kscience.visionforge.Vision import space.kscience.visionforge.Vision
import space.kscience.visionforge.VisionChange import space.kscience.visionforge.VisionChange
import space.kscience.visionforge.VisionManager import space.kscience.visionforge.VisionManager
@ -54,12 +53,12 @@ public class VisionServer internal constructor(
private val application: Application, private val application: Application,
private val rootRoute: String, private val rootRoute: String,
) : Configurable, CoroutineScope by application { ) : Configurable, CoroutineScope by application {
override val config: ObservableMeta = ObservableMeta() override val meta: ObservableMutableMeta = MutableMeta()
public var updateInterval: Long by config.long(300, key = UPDATE_INTERVAL_KEY) public var updateInterval: Long by meta.long(300, key = UPDATE_INTERVAL_KEY)
public var cacheFragments: Boolean by config.boolean(true) public var cacheFragments: Boolean by meta.boolean(true)
public var dataEmbed: Boolean by config.boolean(true, "data.embed".toName()) public var dataEmbed: Boolean by meta.boolean(true, Name.parse("data.embed"))
public var dataFetch: Boolean by config.boolean(false, "data.fetch".toName()) public var dataFetch: Boolean by meta.boolean(false, Name.parse("data.fetch"))
public var dataConnect: Boolean by config.boolean(true, "data.connect".toName()) public var dataConnect: Boolean by meta.boolean(true, Name.parse("data.connect"))
/** /**
* a list of headers that should be applied to all pages * a list of headers that should be applied to all pages
@ -131,14 +130,14 @@ public class VisionServer internal constructor(
?: error("Vision name is not defined in parameters") ?: error("Vision name is not defined in parameters")
application.log.debug("Opened server socket for $name") application.log.debug("Opened server socket for $name")
val vision: Vision = visions[name.toName()] ?: error("Plot with id='$name' not registered") val vision: Vision = visions[Name.parse(name)] ?: error("Plot with id='$name' not registered")
launch { launch {
incoming.consumeEach { incoming.consumeEach {
val change = visionManager.jsonFormat.decodeFromString( val change = visionManager.jsonFormat.decodeFromString(
VisionChange.serializer(), it.data.decodeToString() VisionChange.serializer(), it.data.decodeToString()
) )
vision.update(change) vision.change(change)
} }
} }
@ -161,7 +160,7 @@ public class VisionServer internal constructor(
val name: String = call.request.queryParameters["name"] val name: String = call.request.queryParameters["name"]
?: error("Vision name is not defined in parameters") ?: error("Vision name is not defined in parameters")
val vision: Vision? = visions[name.toName()] val vision: Vision? = visions[Name.parse(name)]
if (vision == null) { if (vision == null) {
call.respond(HttpStatusCode.NotFound, "Vision with name '$name' not found") call.respond(HttpStatusCode.NotFound, "Vision with name '$name' not found")
} else { } else {
@ -232,7 +231,7 @@ public class VisionServer internal constructor(
public companion object { public companion object {
public const val DEFAULT_PAGE: String = "/" public const val DEFAULT_PAGE: String = "/"
public val UPDATE_INTERVAL_KEY: Name = "update.interval".toName() public val UPDATE_INTERVAL_KEY: Name = Name.parse("update.interval")
} }
} }

View File

@ -1,8 +1,6 @@
package space.kscience.visionforge.solid package space.kscience.visionforge.solid
import space.kscience.dataforge.meta.MutableItemProvider import space.kscience.dataforge.meta.*
import space.kscience.dataforge.meta.set
import space.kscience.dataforge.meta.value
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.values.Value import space.kscience.dataforge.values.Value
import space.kscience.dataforge.values.asValue import space.kscience.dataforge.values.asValue
@ -11,11 +9,17 @@ import space.kscience.visionforge.Colors
import space.kscience.visionforge.VisionBuilder import space.kscience.visionforge.VisionBuilder
@VisionBuilder @VisionBuilder
public class ColorAccessor(private val parent: MutableItemProvider, private val colorKey: Name) { public class ColorAccessor(private val parent: MutableMetaProvider, private val colorKey: Name) {
public var value: Value? public var value: Value?
get() = parent.getItem(colorKey).value get() = parent.getMeta(colorKey)?.value
set(value) { set(value) {
parent[colorKey] = value parent.setValue(colorKey,value)
}
public var item: Meta?
get() = parent.getMeta(colorKey)
set(value) {
parent.setMeta(colorKey,value)
} }
} }

View File

@ -31,7 +31,7 @@ public inline fun VisionContainerBuilder<Solid>.composite(
if (children.size != 2) error("Composite requires exactly two children") if (children.size != 2) error("Composite requires exactly two children")
return Composite(type, children[0], children[1]).also { composite -> return Composite(type, children[0], children[1]).also { composite ->
composite.configure { composite.configure {
update(group.meta) update(group.meta())
} }
if (group.position != null) { if (group.position != null) {
composite.position = group.position composite.position = group.position

View File

@ -2,7 +2,8 @@ package space.kscience.visionforge.solid
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import space.kscience.dataforge.meta.ObservableMeta import space.kscience.dataforge.meta.MutableMeta
import space.kscience.dataforge.meta.ObservableMutableMeta
import space.kscience.visionforge.* import space.kscience.visionforge.*
import kotlin.math.PI import kotlin.math.PI
import kotlin.math.cos import kotlin.math.cos
@ -95,7 +96,7 @@ public class Extruded(
public class ExtrudeBuilder( public class ExtrudeBuilder(
public var shape: List<Point2D> = emptyList(), public var shape: List<Point2D> = emptyList(),
public var layers: ArrayList<Layer> = ArrayList(), public var layers: ArrayList<Layer> = ArrayList(),
config: ObservableMeta = ObservableMeta() config: ObservableMutableMeta = MutableMeta()
) : SimpleVisionPropertyContainer<Extruded>(config) { ) : SimpleVisionPropertyContainer<Extruded>(config) {
public fun shape(block: Shape2DBuilder.() -> Unit) { public fun shape(block: Shape2DBuilder.() -> Unit) {
this.shape = Shape2DBuilder().apply(block).build() this.shape = Shape2DBuilder().apply(block).build()
@ -105,7 +106,7 @@ public class ExtrudeBuilder(
layers.add(Layer(x.toFloat(), y.toFloat(), z.toFloat(), scale.toFloat())) layers.add(Layer(x.toFloat(), y.toFloat(), z.toFloat(), scale.toFloat()))
} }
internal fun build(): Extruded = Extruded(shape, layers).apply { configure(config) } internal fun build(): Extruded = Extruded(shape, layers).apply { configure(meta()) }
} }
@VisionBuilder @VisionBuilder

View File

@ -2,13 +2,12 @@ package space.kscience.visionforge.solid
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import space.kscience.dataforge.meta.number
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.asName
import space.kscience.dataforge.names.plus import space.kscience.dataforge.names.plus
import space.kscience.visionforge.VisionBuilder import space.kscience.visionforge.VisionBuilder
import space.kscience.visionforge.VisionContainerBuilder import space.kscience.visionforge.VisionContainerBuilder
import space.kscience.visionforge.allProperties import space.kscience.visionforge.numberProperty
import space.kscience.visionforge.set import space.kscience.visionforge.set
@Serializable @Serializable
@ -16,8 +15,8 @@ import space.kscience.visionforge.set
public class PolyLine(public val points: List<Point3D>) : SolidBase(), Solid { public class PolyLine(public val points: List<Point3D>) : SolidBase(), Solid {
//var lineType by string() //var lineType by string()
public var thickness: Number by allProperties(inherit = false).number(1.0, public var thickness: Number by numberProperty(name = SolidMaterial.MATERIAL_KEY + THICKNESS_KEY) { 1.0 }
key = SolidMaterial.MATERIAL_KEY + THICKNESS_KEY)
public companion object { public companion object {
public val THICKNESS_KEY: Name = "thickness".asName() public val THICKNESS_KEY: Name = "thickness".asName()

View File

@ -1,7 +1,7 @@
package space.kscience.visionforge.solid package space.kscience.visionforge.solid
import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.*
import space.kscience.dataforge.meta.descriptors.NodeDescriptor import space.kscience.dataforge.meta.descriptors.*
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.asName
import space.kscience.dataforge.names.plus import space.kscience.dataforge.names.plus
@ -35,7 +35,7 @@ import kotlin.reflect.KProperty
*/ */
public interface Solid : Vision { public interface Solid : Vision {
override val descriptor: NodeDescriptor get() = Companion.descriptor override val descriptor: MetaDescriptor get() = Companion.descriptor
public companion object { public companion object {
// val SELECTED_KEY = "selected".asName() // val SELECTED_KEY = "selected".asName()
@ -69,39 +69,38 @@ public interface Solid : Vision {
public val Y_SCALE_KEY: Name = SCALE_KEY + Y_KEY public val Y_SCALE_KEY: Name = SCALE_KEY + Y_KEY
public val Z_SCALE_KEY: Name = SCALE_KEY + Z_KEY public val Z_SCALE_KEY: Name = SCALE_KEY + Z_KEY
public val descriptor: NodeDescriptor by lazy { public val descriptor: MetaDescriptor by lazy {
NodeDescriptor { MetaDescriptor {
value(VISIBLE_KEY) { value(VISIBLE_KEY, ValueType.BOOLEAN) {
inherited = false inherited = false
type(ValueType.BOOLEAN)
default(true) default(true)
} }
//TODO replace by descriptor merge //TODO replace by descriptor merge
value(Vision.STYLE_KEY) { value(Vision.STYLE_KEY, ValueType.STRING) {
type(ValueType.STRING)
multiple = true multiple = true
hide() hide()
} }
node(POSITION_KEY){ node(POSITION_KEY) {
hide() hide()
} }
node(ROTATION_KEY){ node(ROTATION_KEY) {
hide() hide()
} }
node(SCALE_KEY){ node(SCALE_KEY) {
hide() hide()
} }
value(DETAIL_KEY) { value(DETAIL_KEY, ValueType.NUMBER) {
type(ValueType.NUMBER)
hide() hide()
} }
item(SolidMaterial.MATERIAL_KEY.toString(), SolidMaterial.descriptor) item(SolidMaterial.MATERIAL_KEY.toString(), SolidMaterial){
valueRequirement = ValueRequirement.ABSENT
}
enum(ROTATION_ORDER_KEY, default = RotationOrder.XYZ) { enum(ROTATION_ORDER_KEY, default = RotationOrder.XYZ) {
hide() hide()
@ -115,7 +114,7 @@ public interface Solid : Vision {
* Get the layer number this solid belongs to. Return 0 if layer is not defined. * Get the layer number this solid belongs to. Return 0 if layer is not defined.
*/ */
public var Solid.layer: Int public var Solid.layer: Int
get() = allProperties().getItem(LAYER_KEY).int ?: 0 get() = getProperty(LAYER_KEY, inherit = true).int ?: 0
set(value) { set(value) {
setProperty(LAYER_KEY, value) setProperty(LAYER_KEY, value)
} }
@ -136,7 +135,7 @@ public enum class RotationOrder {
*/ */
public var Solid.rotationOrder: RotationOrder public var Solid.rotationOrder: RotationOrder
get() = getProperty(Solid.ROTATION_ORDER_KEY).enum<RotationOrder>() ?: RotationOrder.XYZ get() = getProperty(Solid.ROTATION_ORDER_KEY).enum<RotationOrder>() ?: RotationOrder.XYZ
set(value) = setProperty(Solid.ROTATION_ORDER_KEY, value.name.asValue()) set(value) = setPropertyValue(Solid.ROTATION_ORDER_KEY, value.name.asValue())
/** /**
@ -144,7 +143,7 @@ public var Solid.rotationOrder: RotationOrder
*/ */
public var Solid.detail: Int? public var Solid.detail: Int?
get() = getProperty(DETAIL_KEY, false).int get() = getProperty(DETAIL_KEY, false).int
set(value) = setProperty(DETAIL_KEY, value?.asValue()) set(value) = setPropertyValue(DETAIL_KEY, value?.asValue())
/** /**
* If this property is true, the object will be ignored on render. * If this property is true, the object will be ignored on render.
@ -152,7 +151,7 @@ public var Solid.detail: Int?
*/ */
public var Vision.ignore: Boolean? public var Vision.ignore: Boolean?
get() = getProperty(IGNORE_KEY, false).boolean get() = getProperty(IGNORE_KEY, false).boolean
set(value) = setProperty(IGNORE_KEY, value?.asValue()) set(value) = setPropertyValue(IGNORE_KEY, value?.asValue())
//var VisualObject.selected: Boolean? //var VisualObject.selected: Boolean?
// get() = getProperty(SELECTED_KEY).boolean // get() = getProperty(SELECTED_KEY).boolean
@ -182,7 +181,7 @@ internal fun point(name: Name, default: Float): ReadWriteProperty<Solid, Point3D
override fun setValue(thisRef: Solid, property: KProperty<*>, value: Point3D?) { override fun setValue(thisRef: Solid, property: KProperty<*>, value: Point3D?) {
if (value == null) { if (value == null) {
thisRef.setProperty(name, null) thisRef.setPropertyNode(name, null)
} else { } else {
thisRef.setProperty(name + X_KEY, value.x) thisRef.setProperty(name + X_KEY, value.x)
thisRef.setProperty(name + Y_KEY, value.y) thisRef.setProperty(name + Y_KEY, value.y)

View File

@ -2,17 +2,17 @@ package space.kscience.visionforge.solid
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import space.kscience.dataforge.meta.descriptors.NodeDescriptor import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.visionforge.VisionBase import space.kscience.visionforge.VisionBase
import space.kscience.visionforge.VisionChange import space.kscience.visionforge.VisionChange
@Serializable @Serializable
@SerialName("solid") @SerialName("solid")
public open class SolidBase : VisionBase(), Solid { public open class SolidBase : VisionBase(), Solid {
override val descriptor: NodeDescriptor get() = Solid.descriptor override val descriptor: MetaDescriptor get() = Solid.descriptor
override fun update(change: VisionChange) { override fun change(change: VisionChange) {
updatePosition(change.properties) updatePosition(change.properties)
super.update(change) super.change(change)
} }
} }

View File

@ -2,7 +2,7 @@ package space.kscience.visionforge.solid
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import space.kscience.dataforge.meta.descriptors.NodeDescriptor import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.NameToken import space.kscience.dataforge.names.NameToken
import space.kscience.visionforge.* import space.kscience.visionforge.*
@ -40,7 +40,7 @@ public class SolidGroup : VisionGroupBase(), Solid, PrototypeHolder {
} }
override val descriptor: NodeDescriptor get() = Solid.descriptor override val descriptor: MetaDescriptor get() = Solid.descriptor
/** /**
* Get a prototype redirecting the request to the parent if prototype is not found. * Get a prototype redirecting the request to the parent if prototype is not found.
@ -60,9 +60,9 @@ public class SolidGroup : VisionGroupBase(), Solid, PrototypeHolder {
override fun createGroup(): SolidGroup = SolidGroup() override fun createGroup(): SolidGroup = SolidGroup()
override fun update(change: VisionChange) { override fun change(change: VisionChange) {
updatePosition(change.properties) updatePosition(change.properties)
super.update(change) super.change(change)
} }
public companion object { public companion object {

View File

@ -1,8 +1,8 @@
package space.kscience.visionforge.solid package space.kscience.visionforge.solid
import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.*
import space.kscience.dataforge.meta.descriptors.NodeDescriptor import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.meta.descriptors.attributes import space.kscience.dataforge.meta.descriptors.value
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.asName
import space.kscience.dataforge.names.plus import space.kscience.dataforge.names.plus
@ -26,6 +26,8 @@ public class SolidMaterial : Scheme() {
*/ */
public val specularColor: ColorAccessor = ColorAccessor(this, SPECULAR_COLOR_KEY) public val specularColor: ColorAccessor = ColorAccessor(this, SPECULAR_COLOR_KEY)
public val emissiveColor: ColorAccessor = ColorAccessor(this, "emissiveColor".asName())
/** /**
* Opacity * Opacity
*/ */
@ -48,43 +50,37 @@ public class SolidMaterial : Scheme() {
public val WIREFRAME_KEY: Name = "wireframe".asName() public val WIREFRAME_KEY: Name = "wireframe".asName()
public val MATERIAL_WIREFRAME_KEY: Name = MATERIAL_KEY + WIREFRAME_KEY public val MATERIAL_WIREFRAME_KEY: Name = MATERIAL_KEY + WIREFRAME_KEY
public override val descriptor: NodeDescriptor by lazy { public override val descriptor: MetaDescriptor by lazy {
//must be lazy to avoid initialization bug //must be lazy to avoid initialization bug
NodeDescriptor { MetaDescriptor {
inherited = true inherited = true
usesStyles = true usesStyles = true
value(COLOR_KEY) { value(COLOR_KEY, ValueType.STRING, ValueType.NUMBER) {
inherited = true inherited = true
usesStyles = true usesStyles = true
type(ValueType.STRING, ValueType.NUMBER)
widgetType = "color" widgetType = "color"
} }
value(SPECULAR_COLOR_KEY) { value(SPECULAR_COLOR_KEY, ValueType.STRING, ValueType.NUMBER) {
inherited = true inherited = true
usesStyles = true usesStyles = true
type(ValueType.STRING, ValueType.NUMBER)
widgetType = "color" widgetType = "color"
hide() hide()
} }
value(OPACITY_KEY) { value(OPACITY_KEY, ValueType.NUMBER) {
inherited = true inherited = true
usesStyles = true usesStyles = true
type(ValueType.NUMBER)
default(1.0) default(1.0)
attributes { attributes["min"] = 0.0
this["min"] = 0.0 attributes["max"] = 1.0
this["max"] = 1.0 attributes["step"] = 0.1
this["step"] = 0.1
}
widgetType = "slider" widgetType = "slider"
} }
value(WIREFRAME_KEY) { value(WIREFRAME_KEY, ValueType.BOOLEAN) {
inherited = true inherited = true
usesStyles = true usesStyles = true
type(ValueType.BOOLEAN)
default(false) default(false)
} }
} }
@ -94,21 +90,23 @@ public class SolidMaterial : Scheme() {
public val Solid.color: ColorAccessor public val Solid.color: ColorAccessor
get() = ColorAccessor( get() = ColorAccessor(
allProperties(inherit = true), meta(inherit = true),
MATERIAL_COLOR_KEY MATERIAL_COLOR_KEY
) )
public var Solid.material: SolidMaterial? public var Solid.material: SolidMaterial?
get() = getProperty(MATERIAL_KEY, inherit = true).node?.let { SolidMaterial.read(it) } get() = getProperty(MATERIAL_KEY, inherit = true)?.let { SolidMaterial.read(it) }
set(value) = setProperty(MATERIAL_KEY, value?.rootNode) set(value) = setPropertyNode(MATERIAL_KEY, value?.meta)
@VisionBuilder @VisionBuilder
public fun Solid.material(builder: SolidMaterial.() -> Unit) { public fun Solid.material(builder: SolidMaterial.() -> Unit) {
ownProperties.getChild(MATERIAL_KEY).update(SolidMaterial, builder) configure(MATERIAL_KEY){
updateWith(SolidMaterial,builder)
}
} }
public var Solid.opacity: Number? public var Solid.opacity: Number?
get() = getProperty(MATERIAL_OPACITY_KEY, inherit = true).number get() = getProperty(MATERIAL_OPACITY_KEY, inherit = true).number
set(value) { set(value) {
setProperty(MATERIAL_OPACITY_KEY, value?.asValue()) setPropertyValue(MATERIAL_OPACITY_KEY, value?.asValue())
} }

View File

@ -4,11 +4,11 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.mapNotNull
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import space.kscience.dataforge.meta.MetaItem import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.asMetaItem import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.meta.descriptors.NodeDescriptor
import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.names.* import space.kscience.dataforge.names.*
import space.kscience.dataforge.values.Value
import space.kscience.visionforge.* import space.kscience.visionforge.*
@ -37,7 +37,7 @@ private fun SolidReference.getRefProperty(
inherit: Boolean, inherit: Boolean,
includeStyles: Boolean, includeStyles: Boolean,
includeDefaults: Boolean, includeDefaults: Boolean,
): MetaItem? = if (!inherit && !includeStyles && !includeDefaults) { ): Meta? = if (!inherit && !includeStyles && !includeDefaults) {
getOwnProperty(name) getOwnProperty(name)
} else { } else {
buildList { buildList {
@ -88,9 +88,9 @@ public class SolidReferenceGroup(
inherit: Boolean, inherit: Boolean,
includeStyles: Boolean, includeStyles: Boolean,
includeDefaults: Boolean, includeDefaults: Boolean,
): MetaItem? = getRefProperty(name, inherit, includeStyles, includeDefaults) ): Meta? = getRefProperty(name, inherit, includeStyles, includeDefaults)
override val descriptor: NodeDescriptor get() = prototype.descriptor override val descriptor: MetaDescriptor get() = prototype.descriptor
/** /**
@ -118,11 +118,15 @@ public class SolidReferenceGroup(
ReferenceChild(owner, refName + key.asName()) ReferenceChild(owner, refName + key.asName())
} ?: emptyMap() } ?: emptyMap()
override fun getOwnProperty(name: Name): MetaItem? = override fun getOwnProperty(name: Name): Meta? =
owner.getOwnProperty(childPropertyName(refName, name)) owner.getOwnProperty(childPropertyName(refName, name))
override fun setProperty(name: Name, item: MetaItem?, notify: Boolean) { override fun setPropertyNode(name: Name, node: Meta?, notify: Boolean) {
owner.setProperty(childPropertyName(refName, name), item, notify) owner.setPropertyNode(childPropertyName(refName, name), node, notify)
}
override fun setPropertyValue(name: Name, value: Value?, notify: Boolean) {
owner.setPropertyValue(childPropertyName(refName, name), value, notify)
} }
override fun getProperty( override fun getProperty(
@ -130,7 +134,7 @@ public class SolidReferenceGroup(
inherit: Boolean, inherit: Boolean,
includeStyles: Boolean, includeStyles: Boolean,
includeDefaults: Boolean, includeDefaults: Boolean,
): MetaItem? = getRefProperty(name, inherit, includeStyles, includeDefaults) ): Meta? = getRefProperty(name, inherit, includeStyles, includeDefaults)
override var parent: VisionGroup? override var parent: VisionGroup?
get() { get() {
@ -155,13 +159,13 @@ public class SolidReferenceGroup(
owner.invalidateProperty(childPropertyName(refName, propertyName)) owner.invalidateProperty(childPropertyName(refName, propertyName))
} }
override fun update(change: VisionChange) { override fun change(change: VisionChange) {
change.properties?.let { change.properties?.let {
updateProperties(Name.EMPTY, it.asMetaItem()) updateProperties(Name.EMPTY, it)
} }
} }
override val descriptor: NodeDescriptor get() = prototype.descriptor override val descriptor: MetaDescriptor get() = prototype.descriptor
} }
@ -184,7 +188,7 @@ public fun SolidGroup.ref(
public fun SolidGroup.ref( public fun SolidGroup.ref(
name: String, name: String,
obj: Solid, obj: Solid,
templateName: Name = name.toName(), templateName: Name = Name.parse(name),
): SolidReferenceGroup { ): SolidReferenceGroup {
val existing = getPrototype(templateName) val existing = getPrototype(templateName)
if (existing == null) { if (existing == null) {

View File

@ -95,13 +95,13 @@ public fun MutablePoint3D.normalizeInPlace() {
z /= norm z /= norm
} }
internal fun ItemProvider.point3D(default: Float = 0f) = object : Point3D { internal fun MetaProvider.point3D(default: Float = 0f) = object : Point3D {
override val x: Float by float(default) override val x: Float by float(default)
override val y: Float by float(default) override val y: Float by float(default)
override val z: Float by float(default) override val z: Float by float(default)
} }
public fun Point3D.toMeta(): MetaBuilder = Meta { public fun Point3D.toMeta(): Meta = Meta {
X_KEY put x X_KEY put x
Y_KEY put y Y_KEY put y
Z_KEY put z Z_KEY put z
@ -115,7 +115,7 @@ internal fun Meta.toVector(default: Float = 0f) = Point3D(
) )
internal fun Solid.updatePosition(meta: Meta?) { internal fun Solid.updatePosition(meta: Meta?) {
meta[Solid.POSITION_KEY].node?.toVector()?.let { position = it } meta?.get(Solid.POSITION_KEY)?.toVector()?.let { position = it }
meta[Solid.ROTATION_KEY].node?.toVector()?.let { rotation = it } meta?.get(Solid.ROTATION_KEY)?.toVector()?.let { rotation = it }
meta[Solid.SCALE_KEY].node?.toVector(1f)?.let { scale = it } meta?.get(Solid.SCALE_KEY)?.toVector(1f)?.let { scale = it }
} }

View File

@ -3,7 +3,7 @@ package space.kscience.visionforge.solid.specifications
import space.kscience.dataforge.meta.Scheme import space.kscience.dataforge.meta.Scheme
import space.kscience.dataforge.meta.SchemeSpec import space.kscience.dataforge.meta.SchemeSpec
import space.kscience.dataforge.meta.boolean import space.kscience.dataforge.meta.boolean
import space.kscience.dataforge.meta.descriptors.NodeDescriptor import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.meta.double import space.kscience.dataforge.meta.double
import space.kscience.visionforge.value import space.kscience.visionforge.value
@ -16,8 +16,8 @@ public class Axes : Scheme() {
public const val AXIS_SIZE: Double = 1000.0 public const val AXIS_SIZE: Double = 1000.0
public const val AXIS_WIDTH: Double = 3.0 public const val AXIS_WIDTH: Double = 3.0
override val descriptor: NodeDescriptor by lazy { override val descriptor: MetaDescriptor by lazy {
NodeDescriptor { MetaDescriptor {
value(Axes::visible){ value(Axes::visible){
default(false) default(false)
} }

View File

@ -2,7 +2,7 @@ package space.kscience.visionforge.solid.specifications
import space.kscience.dataforge.meta.Scheme import space.kscience.dataforge.meta.Scheme
import space.kscience.dataforge.meta.SchemeSpec import space.kscience.dataforge.meta.SchemeSpec
import space.kscience.dataforge.meta.descriptors.NodeDescriptor import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.meta.double import space.kscience.dataforge.meta.double
import space.kscience.dataforge.meta.int import space.kscience.dataforge.meta.int
import space.kscience.visionforge.value import space.kscience.visionforge.value
@ -27,8 +27,8 @@ public class Camera : Scheme() {
public const val FAR_CLIP: Double = 10000.0 public const val FAR_CLIP: Double = 10000.0
public const val FIELD_OF_VIEW: Int = 75 public const val FIELD_OF_VIEW: Int = 75
override val descriptor: NodeDescriptor by lazy { override val descriptor: MetaDescriptor by lazy {
NodeDescriptor { MetaDescriptor {
value(Camera::fov){ value(Camera::fov){
default(FIELD_OF_VIEW) default(FIELD_OF_VIEW)
} }

View File

@ -1,8 +1,7 @@
package space.kscience.visionforge.solid.specifications package space.kscience.visionforge.solid.specifications
import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.*
import space.kscience.dataforge.meta.descriptors.NodeDescriptor import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.meta.descriptors.attributes
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.values.ValueType import space.kscience.dataforge.values.ValueType
import space.kscience.visionforge.hide import space.kscience.visionforge.hide
@ -16,30 +15,26 @@ public class Clipping : Scheme() {
public var z: Double? by double() public var z: Double? by double()
public companion object : SchemeSpec<Clipping>(::Clipping) { public companion object : SchemeSpec<Clipping>(::Clipping) {
override val descriptor: NodeDescriptor = NodeDescriptor { override val descriptor: MetaDescriptor = MetaDescriptor {
value(Clipping::x) { value(Clipping::x) {
widgetType = "range" widgetType = "range"
attributes { attributes["min"] = 0.0
set("min", 0.0) attributes["max"] = 1.0
set("max", 1.0) attributes["step"] = 0.01
set("step", 0.01)
}
} }
value(Clipping::y) { value(Clipping::y) {
widgetType = "range" widgetType = "range"
attributes { attributes["min"] = 0.0
set("min", 0.0) attributes["max"] = 1.0
set("max", 1.0) attributes["step"] = 0.01
set("step", 0.01)
}
} }
value(Clipping::z) { value(Clipping::z) {
widgetType = "range" widgetType = "range"
attributes { attributes["min"] = 0.0
set("min", 0.0) attributes["max"] = 1.0
set("max", 1.0) attributes["step"] = 0.01
set("step", 0.01)
}
} }
} }
} }
@ -55,7 +50,7 @@ public class CanvasSize : Scheme() {
public var maxHeight: Number by number { maxSize } public var maxHeight: Number by number { maxSize }
public companion object : SchemeSpec<CanvasSize>(::CanvasSize) { public companion object : SchemeSpec<CanvasSize>(::CanvasSize) {
override val descriptor: NodeDescriptor = NodeDescriptor { override val descriptor: MetaDescriptor = MetaDescriptor {
value(CanvasSize::minSize) value(CanvasSize::minSize)
value(CanvasSize::minWith) value(CanvasSize::minWith)
value(CanvasSize::minHeight) value(CanvasSize::minHeight)
@ -82,8 +77,8 @@ public class Canvas3DOptions : Scheme() {
public companion object : SchemeSpec<Canvas3DOptions>(::Canvas3DOptions) { public companion object : SchemeSpec<Canvas3DOptions>(::Canvas3DOptions) {
override val descriptor: NodeDescriptor by lazy { override val descriptor: MetaDescriptor by lazy {
NodeDescriptor { MetaDescriptor {
scheme(Canvas3DOptions::axes, Axes) scheme(Canvas3DOptions::axes, Axes)
scheme(Canvas3DOptions::light, Light) scheme(Canvas3DOptions::light, Light)
@ -104,7 +99,7 @@ public class Canvas3DOptions : Scheme() {
multiple = true multiple = true
default(listOf(0)) default(listOf(0))
widgetType = "multiSelect" widgetType = "multiSelect"
allow(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10) allowedValues(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
} }
scheme(Canvas3DOptions::clipping, Clipping) scheme(Canvas3DOptions::clipping, Clipping)
} }

View File

@ -1,12 +1,9 @@
package space.kscience.visionforge.solid.transform package space.kscience.visionforge.solid.transform
import space.kscience.dataforge.meta.itemSequence import space.kscience.dataforge.meta.update
import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.asName
import space.kscience.visionforge.MutableVisionGroup import space.kscience.visionforge.*
import space.kscience.visionforge.Vision
import space.kscience.visionforge.VisionGroup
import space.kscience.visionforge.meta
import space.kscience.visionforge.solid.* import space.kscience.visionforge.solid.*
private operator fun Number.plus(other: Number) = toFloat() + other.toFloat() private operator fun Number.plus(other: Number) = toFloat() + other.toFloat()
@ -24,10 +21,8 @@ internal fun Vision.updateFrom(other: Vision): Vision {
scaleX *= other.scaleX scaleX *= other.scaleX
scaleY *= other.scaleY scaleY *= other.scaleY
scaleZ *= other.scaleZ scaleZ *= other.scaleZ
other.meta.itemSequence().forEach { (name, item) -> configure{
if (getProperty(name) == null) { update(other.meta())
setProperty(name, item)
}
} }
} }
return this return this

View File

@ -1,9 +1,8 @@
package space.kscience.visionforge.solid package space.kscience.visionforge.solid
import space.kscience.dataforge.meta.MetaItemNode
import space.kscience.dataforge.meta.getIndexed import space.kscience.dataforge.meta.getIndexed
import space.kscience.dataforge.meta.node import space.kscience.dataforge.meta.node
import space.kscience.dataforge.meta.toMetaItem import space.kscience.dataforge.meta.toMeta
import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.misc.DFExperimental
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -29,9 +28,9 @@ class ConvexTest {
val convex = group.children.values.first() as Convex val convex = group.children.values.first() as Convex
val json = Solids.jsonForSolids.encodeToJsonElement(Convex.serializer(), convex) val json = Solids.jsonForSolids.encodeToJsonElement(Convex.serializer(), convex)
val meta = json.toMetaItem().node!! val meta = json.toMeta()
val points = meta.getIndexed("points").values.map { (it as MetaItemNode<*>).node.point3D() } val points = meta.getIndexed("points").values.map { it.point3D() }
assertEquals(8, points.count()) assertEquals(8, points.count())
assertEquals(8, convex.points.size) assertEquals(8, convex.points.size)

View File

@ -1,6 +1,5 @@
package space.kscience.visionforge.solid package space.kscience.visionforge.solid
import space.kscience.dataforge.meta.descriptors.ValueDescriptor
import space.kscience.dataforge.meta.descriptors.get import space.kscience.dataforge.meta.descriptors.get
import space.kscience.dataforge.values.ValueType import space.kscience.dataforge.values.ValueType
import space.kscience.visionforge.solid.specifications.Canvas3DOptions import space.kscience.visionforge.solid.specifications.Canvas3DOptions
@ -16,7 +15,7 @@ class DescriptorTest {
val axesSize = descriptor["axes.size"] val axesSize = descriptor["axes.size"]
assertNotNull(axesSize) assertNotNull(axesSize)
assertTrue { assertTrue {
ValueType.NUMBER in (axesSize as ValueDescriptor).type!! ValueType.NUMBER in axesSize.valueTypes!!
} }
} }
} }

View File

@ -12,7 +12,7 @@ class PropertyTest {
fun testInheritedProperty() { fun testInheritedProperty() {
var box: Box? = null var box: Box? = null
val group = SolidGroup().apply { val group = SolidGroup().apply {
setProperty("test", 22) setPropertyNode("test", 22)
group { group {
box = box(100, 100, 100) box = box(100, 100, 100)
} }

View File

@ -1,7 +1,6 @@
package space.kscience.visionforge.solid package space.kscience.visionforge.solid
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.toName
import space.kscience.visionforge.MutableVisionGroup import space.kscience.visionforge.MutableVisionGroup
import space.kscience.visionforge.get import space.kscience.visionforge.get
import space.kscience.visionforge.meta import space.kscience.visionforge.meta
@ -14,7 +13,7 @@ import kotlin.test.assertEquals
*/ */
fun SolidGroup.refGroup( fun SolidGroup.refGroup(
name: String, name: String,
templateName: Name = name.toName(), templateName: Name = Name.parse(name),
block: MutableVisionGroup.() -> Unit block: MutableVisionGroup.() -> Unit
): SolidReferenceGroup { ): SolidReferenceGroup {
val group = SolidGroup().apply(block) val group = SolidGroup().apply(block)
@ -30,10 +29,10 @@ class SerializationTest {
x = 100 x = 100
z = -100 z = -100
} }
val string = Solids.encodeToString(cube) val string = Solids.encodeToString(cube)
println(string) println(string)
val newCube = Solids.decodeFromString(string) val newCube = Solids.decodeFromString(string)
assertEquals(cube.meta, newCube.meta) assertEquals(cube.meta(), newCube.meta())
} }
@Test @Test
@ -43,10 +42,10 @@ class SerializationTest {
x = 100 x = 100
z = -100 z = -100
} }
val group = SolidGroup{ val group = SolidGroup {
ref("cube", cube) ref("cube", cube)
refGroup("pg", "pg.content".toName()){ refGroup("pg", Name.parse("pg.content")) {
sphere(50){ sphere(50) {
x = -100 x = -100
} }
} }
@ -54,7 +53,7 @@ class SerializationTest {
val string = Solids.encodeToString(group) val string = Solids.encodeToString(group)
println(string) println(string)
val reconstructed = Solids.decodeFromString(string) as SolidGroup val reconstructed = Solids.decodeFromString(string) as SolidGroup
assertEquals(group["cube"]?.meta, reconstructed["cube"]?.meta) assertEquals(group["cube"]?.meta(), reconstructed["cube"]?.meta())
} }
} }

View File

@ -2,8 +2,9 @@ package space.kscience.visionforge.solid
import space.kscience.dataforge.context.Global import space.kscience.dataforge.context.Global
import space.kscience.dataforge.context.fetch import space.kscience.dataforge.context.fetch
import space.kscience.dataforge.meta.MetaItem import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.names.toName import space.kscience.dataforge.names.asName
import space.kscience.dataforge.values.asValue
import space.kscience.visionforge.VisionChange import space.kscience.visionforge.VisionChange
import space.kscience.visionforge.get import space.kscience.visionforge.get
import kotlin.test.Test import kotlin.test.Test
@ -24,10 +25,10 @@ class VisionUpdateTest {
color(123) color(123)
box(100,100,100) box(100,100,100)
} }
propertyChanged("top".toName(), SolidMaterial.MATERIAL_COLOR_KEY, MetaItem.of("red")) propertyChanged("top".asName(), SolidMaterial.MATERIAL_COLOR_KEY, Meta("red".asValue()))
propertyChanged("origin".toName(), SolidMaterial.MATERIAL_COLOR_KEY, MetaItem.of("red")) propertyChanged("origin".asName(), SolidMaterial.MATERIAL_COLOR_KEY, Meta("red".asValue()))
} }
targetVision.update(dif) targetVision.change(dif)
assertTrue { targetVision["top"] is SolidGroup } assertTrue { targetVision["top"] is SolidGroup }
assertEquals("red", (targetVision["origin"] as Solid).color.string) // Should work assertEquals("red", (targetVision["origin"] as Solid).color.string) // Should work
assertEquals("#00007b", (targetVision["top"] as Solid).color.string) // new item always takes precedence assertEquals("#00007b", (targetVision["top"] as Solid).color.string) // new item always takes precedence
@ -40,8 +41,8 @@ class VisionUpdateTest {
color(123) color(123)
box(100,100,100) box(100,100,100)
} }
propertyChanged("top".toName(), SolidMaterial.MATERIAL_COLOR_KEY, MetaItem.of("red")) propertyChanged("top".asName(), SolidMaterial.MATERIAL_COLOR_KEY, Meta("red".asValue()))
propertyChanged("origin".toName(), SolidMaterial.MATERIAL_COLOR_KEY, MetaItem.of("red")) propertyChanged("origin".asName(), SolidMaterial.MATERIAL_COLOR_KEY, Meta("red".asValue()))
} }
val serialized = visionManager.jsonFormat.encodeToString(VisionChange.serializer(), change) val serialized = visionManager.jsonFormat.encodeToString(VisionChange.serializer(), change)
println(serialized) println(serialized)

View File

@ -90,7 +90,7 @@ public fun Mesh.applyEdges(obj: Solid) {
MeshThreeFactory.EDGES_MATERIAL_KEY, MeshThreeFactory.EDGES_MATERIAL_KEY,
inherit = true, inherit = true,
includeStyles = true includeStyles = true
).node, ),
true true
) )
if (edges == null) { if (edges == null) {

View File

@ -168,10 +168,10 @@ public class ThreeCanvas(
} }
//Clipping planes //Clipping planes
options.onChange(this@ThreeCanvas) { name, _, _ -> options.meta.onChange(this@ThreeCanvas) { name->
if (name.startsWith(Canvas3DOptions::clipping.name.asName())) { if (name.startsWith(Canvas3DOptions::clipping.name.asName())) {
val clipping = options.clipping val clipping = options.clipping
if (!clipping.isEmpty()) { if (!clipping.meta.isEmpty()) {
renderer.localClippingEnabled = true renderer.localClippingEnabled = true
boundingBox?.let { boundingBox -> boundingBox?.let { boundingBox ->
val xClippingPlane = clipping.x?.let { val xClippingPlane = clipping.x?.let {
@ -212,9 +212,9 @@ public class ThreeCanvas(
private fun Object3D.fullName(): Name { private fun Object3D.fullName(): Name {
if (root == null) error("Can't resolve element name without the root") if (root == null) error("Can't resolve element name without the root")
return if (parent == root) { return if (parent == root) {
name.toName() Name.parse(name)
} else { } else {
(parent?.fullName() ?: Name.EMPTY) + name.toName() (parent?.fullName() ?: Name.EMPTY) + Name.parse(name)
} }
} }
@ -237,7 +237,7 @@ public class ThreeCanvas(
private fun buildLight(spec: Light?): info.laht.threekt.lights.Light = AmbientLight(0x404040) private fun buildLight(spec: Light?): info.laht.threekt.lights.Light = AmbientLight(0x404040)
private fun addControls(element: Node, controls: Controls) { private fun addControls(element: Node, controls: Controls) {
when (controls["type"].string) { when (controls.meta["type"].string) {
"trackball" -> TrackballControls(camera, element) "trackball" -> TrackballControls(camera, element)
else -> OrbitControls(camera, element) else -> OrbitControls(camera, element)
} }

View File

@ -4,7 +4,6 @@ import info.laht.threekt.core.BufferGeometry
import info.laht.threekt.core.Object3D import info.laht.threekt.core.Object3D
import info.laht.threekt.math.Color import info.laht.threekt.math.Color
import info.laht.threekt.objects.LineSegments import info.laht.threekt.objects.LineSegments
import space.kscience.dataforge.meta.node
import space.kscience.visionforge.onPropertyChange import space.kscience.visionforge.onPropertyChange
import space.kscience.visionforge.solid.PolyLine import space.kscience.visionforge.solid.PolyLine
import space.kscience.visionforge.solid.color import space.kscience.visionforge.solid.color
@ -20,7 +19,10 @@ public object ThreeLineFactory : ThreeFactory<PolyLine> {
setFromPoints(Array(obj.points.size) { obj.points[it].toVector() }) setFromPoints(Array(obj.points.size) { obj.points[it].toVector() })
} }
val material = ThreeMaterials.getLineMaterial(obj.getProperty(MeshThreeFactory.EDGES_MATERIAL_KEY).node, true) val material = ThreeMaterials.getLineMaterial(
obj.getProperty(MeshThreeFactory.EDGES_MATERIAL_KEY),
true
)
material.linewidth = obj.thickness.toDouble() material.linewidth = obj.thickness.toDouble()
material.color = obj.color.string?.let { Color(it) } ?: DEFAULT_LINE_COLOR material.color = obj.color.string?.let { Color(it) } ?: DEFAULT_LINE_COLOR

View File

@ -13,7 +13,7 @@ import space.kscience.dataforge.values.int
import space.kscience.dataforge.values.string import space.kscience.dataforge.values.string
import space.kscience.visionforge.Colors import space.kscience.visionforge.Colors
import space.kscience.visionforge.Vision import space.kscience.visionforge.Vision
import space.kscience.visionforge.ownProperties import space.kscience.visionforge.meta
import space.kscience.visionforge.solid.SolidMaterial import space.kscience.visionforge.solid.SolidMaterial
@ -41,7 +41,7 @@ public object ThreeMaterials {
private val lineMaterialCache = HashMap<Meta, LineBasicMaterial>() private val lineMaterialCache = HashMap<Meta, LineBasicMaterial>()
private fun buildLineMaterial(meta: Meta): LineBasicMaterial = LineBasicMaterial().apply { private fun buildLineMaterial(meta: Meta): LineBasicMaterial = LineBasicMaterial().apply {
color = meta[SolidMaterial.COLOR_KEY]?.getColor() ?: DEFAULT_LINE_COLOR color = meta[SolidMaterial.COLOR_KEY]?.threeColor() ?: DEFAULT_LINE_COLOR
opacity = meta[SolidMaterial.OPACITY_KEY].double ?: 1.0 opacity = meta[SolidMaterial.OPACITY_KEY].double ?: 1.0
transparent = opacity < 1.0 transparent = opacity < 1.0
linewidth = meta["thickness"].double ?: 1.0 linewidth = meta["thickness"].double ?: 1.0
@ -59,11 +59,12 @@ public object ThreeMaterials {
private val materialCache = HashMap<Meta, Material>() private val materialCache = HashMap<Meta, Material>()
internal fun buildMaterial(meta: Meta): Material { internal fun buildMaterial(meta: Meta): Material {
val material = SolidMaterial.read(meta)
return meta[SolidMaterial.SPECULAR_COLOR_KEY]?.let { specularColor -> return meta[SolidMaterial.SPECULAR_COLOR_KEY]?.let { specularColor ->
MeshPhongMaterial().apply { MeshPhongMaterial().apply {
color = meta[SolidMaterial.COLOR_KEY]?.getColor() ?: DEFAULT_COLOR color = meta[SolidMaterial.COLOR_KEY]?.threeColor() ?: DEFAULT_COLOR
specular = specularColor.getColor() specular = specularColor.threeColor()
emissive = specular emissive = material.emissiveColor.item?.threeColor() ?: specular
reflectivity = 0.5 reflectivity = 0.5
refractionRatio = 1.0 refractionRatio = 1.0
shininess = 100.0 shininess = 100.0
@ -73,7 +74,7 @@ public object ThreeMaterials {
needsUpdate = true needsUpdate = true
} }
} ?: MeshBasicMaterial().apply { } ?: MeshBasicMaterial().apply {
color = meta[SolidMaterial.COLOR_KEY]?.getColor() ?: DEFAULT_COLOR color = meta[SolidMaterial.COLOR_KEY]?.threeColor() ?: DEFAULT_COLOR
opacity = meta[SolidMaterial.OPACITY_KEY]?.double ?: 1.0 opacity = meta[SolidMaterial.OPACITY_KEY]?.double ?: 1.0
transparent = opacity < 1.0 transparent = opacity < 1.0
wireframe = meta[SolidMaterial.WIREFRAME_KEY].boolean ?: false wireframe = meta[SolidMaterial.WIREFRAME_KEY].boolean ?: false
@ -93,22 +94,19 @@ public object ThreeMaterials {
/** /**
* Infer color based on meta item * Infer color based on meta item
*/ */
public fun MetaItem.getColor(): Color { public fun Meta.threeColor(): Color {
return when (this) { return value?.let { value ->
is MetaItemValue -> if (this.value.type == ValueType.NUMBER) { if (value.type == ValueType.NUMBER) {
val int = value.int val int = value.int
Color(int) Color(int)
} else { } else {
Color(this.value.string) Color(value.string)
} }
is MetaItemNode -> { } ?: Color(
Color( this[Colors.RED_KEY]?.int ?: 0,
node[Colors.RED_KEY]?.int ?: 0, this[Colors.GREEN_KEY]?.int ?: 0,
node[Colors.GREEN_KEY]?.int ?: 0, this[Colors.BLUE_KEY]?.int ?: 0
node[Colors.BLUE_KEY]?.int ?: 0 )
)
}
}
} }
private var Material.cached: Boolean private var Material.cached: Boolean
@ -119,7 +117,7 @@ private var Material.cached: Boolean
public fun Mesh.updateMaterial(vision: Vision) { public fun Mesh.updateMaterial(vision: Vision) {
//val meta = vision.getProperty(SolidMaterial.MATERIAL_KEY, inherit = true).node //val meta = vision.getProperty(SolidMaterial.MATERIAL_KEY, inherit = true).node
val ownMaterialMeta = vision.ownProperties[SolidMaterial.MATERIAL_KEY] val ownMaterialMeta = vision.meta()[SolidMaterial.MATERIAL_KEY]
val parentMaterialMeta = vision.parent?.getProperty( val parentMaterialMeta = vision.parent?.getProperty(
SolidMaterial.MATERIAL_KEY, SolidMaterial.MATERIAL_KEY,
inherit = true, inherit = true,
@ -135,7 +133,7 @@ public fun Mesh.updateMaterial(vision: Vision) {
inherit = false, inherit = false,
includeStyles = true, includeStyles = true,
includeDefaults = false includeDefaults = false
).node?.let { )?.let {
ThreeMaterials.cacheMaterial(it) ThreeMaterials.cacheMaterial(it)
} ?: ThreeMaterials.DEFAULT } ?: ThreeMaterials.DEFAULT
} }
@ -143,7 +141,7 @@ public fun Mesh.updateMaterial(vision: Vision) {
vision.getProperty( vision.getProperty(
SolidMaterial.MATERIAL_KEY, SolidMaterial.MATERIAL_KEY,
inherit = true inherit = true
).node?.let { )?.let {
ThreeMaterials.buildMaterial(it) ThreeMaterials.buildMaterial(it)
} ?: ThreeMaterials.DEFAULT } ?: ThreeMaterials.DEFAULT
} }
@ -162,7 +160,7 @@ public fun Mesh.updateMaterialProperty(vision: Vision, propertyName: Name) {
inherit = true, inherit = true,
includeStyles = true, includeStyles = true,
includeDefaults = false includeDefaults = false
)?.getColor() ?: ThreeMaterials.DEFAULT_COLOR )?.threeColor() ?: ThreeMaterials.DEFAULT_COLOR
material.needsUpdate = true material.needsUpdate = true
} }
SolidMaterial.MATERIAL_OPACITY_KEY -> { SolidMaterial.MATERIAL_OPACITY_KEY -> {

View File

@ -143,7 +143,7 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
element, element,
vision as? Solid ?: error("Solid expected but ${vision::class} found"), vision as? Solid ?: error("Solid expected but ${vision::class} found"),
).apply { ).apply {
options.update(meta) options.meta.update(meta)
} }
} }
@ -195,8 +195,4 @@ internal fun Object3D.findChild(name: Name): Object3D? {
name.length == 1 -> this.children.find { it.name == name.tokens.first().toString() } name.length == 1 -> this.children.find { it.name == name.tokens.first().toString() }
else -> findChild(name.tokens.first().asName())?.findChild(name.cutFirst()) else -> findChild(name.tokens.first().asName())?.findChild(name.cutFirst())
} }
}
public fun Context.withThreeJs(): Context = apply {
plugins.fetch(ThreePlugin)
} }

View File

@ -3,9 +3,9 @@ package space.kscience.visionforge.solid.three
import info.laht.threekt.core.BufferGeometry import info.laht.threekt.core.BufferGeometry
import info.laht.threekt.core.Object3D import info.laht.threekt.core.Object3D
import info.laht.threekt.objects.Mesh import info.laht.threekt.objects.Mesh
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.cutFirst import space.kscience.dataforge.names.cutFirst
import space.kscience.dataforge.names.firstOrNull import space.kscience.dataforge.names.firstOrNull
import space.kscience.dataforge.names.toName
import space.kscience.visionforge.onPropertyChange import space.kscience.visionforge.onPropertyChange
import space.kscience.visionforge.solid.Solid import space.kscience.visionforge.solid.Solid
import space.kscience.visionforge.solid.SolidReferenceGroup import space.kscience.visionforge.solid.SolidReferenceGroup
@ -49,7 +49,7 @@ public object ThreeReferenceFactory : ThreeFactory<SolidReferenceGroup> {
obj.onPropertyChange(three.updateScope) { name-> obj.onPropertyChange(three.updateScope) { name->
if (name.firstOrNull()?.body == REFERENCE_CHILD_PROPERTY_PREFIX) { if (name.firstOrNull()?.body == REFERENCE_CHILD_PROPERTY_PREFIX) {
val childName = name.firstOrNull()?.index?.toName() ?: error("Wrong syntax for reference child property: '$name'") val childName = name.firstOrNull()?.index?.let(Name::parse) ?: error("Wrong syntax for reference child property: '$name'")
val propertyName = name.cutFirst() val propertyName = name.cutFirst()
val referenceChild = obj[childName] ?: error("Reference child with name '$childName' not found") val referenceChild = obj[childName] ?: error("Reference child with name '$childName' not found")
val child = object3D.findChild(childName) ?: error("Object child with name '$childName' not found") val child = object3D.findChild(childName) ?: error("Object child with name '$childName' not found")

Some files were not shown because too many files have changed in this diff Show More