From 7b78052f617b6668dded0690e7540ea9c27c7e2c Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 8 Aug 2021 22:17:50 +0300 Subject: [PATCH] A lot of changes --- build.gradle.kts | 2 +- demo/gdml/build.gradle.kts | 5 +- .../visionforge/gdml/GDMLVisionTest.kt | 4 +- .../visionforge/gdml/demo/GdmlFxDemoApp.kt | 6 +- .../kscience/visionforge/solid/demo/demo.kt | 9 +- .../visionforge/solid/demo/VariableBox.kt | 9 +- ui/bootstrap/build.gradle.kts | 2 +- .../bootstrap/visionPropertyEditor.kt | 9 +- .../ThreeViewWithControls.kt | 7 +- .../ringPropertyEditor.kt | 9 +- .../visionforge/ComputedVisionProperties.kt | 53 ++++++ .../space/kscience/visionforge/StyleSheet.kt | 18 +- .../space/kscience/visionforge/Vision.kt | 122 ++++++------- .../space/kscience/visionforge/VisionBase.kt | 165 ++++++++++-------- .../kscience/visionforge/VisionChange.kt | 4 +- .../kscience/visionforge/VisionGroupBase.kt | 12 +- .../kscience/visionforge/VisionProperties.kt | 38 ---- .../visionforge/VisionPropertyContainer.kt | 31 +--- .../kotlin/space/kscience/visionforge/misc.kt | 11 +- .../kscience/visionforge/schemeDesctiptors.kt | 66 ------- .../kscience/visionforge/visionDelegates.kt | 76 ++++---- .../kscience/visionforge/visionDescriptor.kt | 9 - .../kscience/visionforge/html/HtmlTagTest.kt | 11 +- .../visionforge/meta/VisionPropertyTest.kt | 44 +++++ .../kscience/visionforge/VisionClient.kt | 2 +- .../visionforge/editor/FXMetaModel.kt | 154 ++-------------- .../visionforge/editor/MutableMetaEditor.kt | 54 +----- .../editor/VisionEditorFragment.kt | 45 ++--- .../kscience/visionforge/solid/FX3DPlugin.kt | 3 +- .../visionforge/solid/FXReferenceFactory.kt | 2 +- .../solid/VisualObjectFXBinding.kt | 5 +- .../visionforge/markup/VisionOfMarkup.kt | 2 +- .../visionforge/plotly/VisionOfPlotly.kt | 4 +- .../visionforge/three/server/VisionServer.kt | 2 +- .../visionforge/solid/ColorAccessor.kt | 13 +- .../kscience/visionforge/solid/Composite.kt | 15 +- .../kscience/visionforge/solid/Extruded.kt | 5 +- .../space/kscience/visionforge/solid/Solid.kt | 29 ++- .../kscience/visionforge/solid/SolidBase.kt | 4 +- .../kscience/visionforge/solid/SolidGroup.kt | 4 +- .../visionforge/solid/SolidMaterial.kt | 24 ++- .../visionforge/solid/SolidReference.kt | 81 +++------ .../visionforge/solid/specifications/Axes.kt | 2 +- .../solid/specifications/Camera.kt | 16 +- .../solid/specifications/Canvas3DOptions.kt | 27 +-- .../solid/transform/RemoveSingleChild.kt | 3 +- .../visionforge/solid/PropertyTest.kt | 19 +- .../visionforge/solid/SerializationTest.kt | 5 +- .../visionforge/solid/VisionUpdateTest.kt | 2 +- .../solid/three/MeshThreeFactory.kt | 15 +- .../solid/three/ThreeCompositeFactory.kt | 2 +- .../solid/three/ThreeGeometryBuilder.kt | 2 +- .../solid/three/ThreeLabelFactory.kt | 2 +- .../solid/three/ThreeLineFactory.kt | 5 +- .../visionforge/solid/three/ThreeMaterials.kt | 31 ++-- .../visionforge/solid/three/ThreePlugin.kt | 2 +- .../solid/three/ThreeReferenceFactory.kt | 2 +- .../kscience/visionforge/solid/three/csg.kt | 3 +- 58 files changed, 524 insertions(+), 784 deletions(-) create mode 100644 visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/ComputedVisionProperties.kt delete mode 100644 visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionProperties.kt delete mode 100644 visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/schemeDesctiptors.kt create mode 100644 visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/meta/VisionPropertyTest.kt diff --git a/build.gradle.kts b/build.gradle.kts index 22615a5d..ec691447 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,7 +2,7 @@ plugins { id("ru.mipt.npm.gradle.project") } -val dataforgeVersion by extra("0.5.0-dev-9") +val dataforgeVersion by extra("0.5.0-dev-10") val fxVersion by extra("11") allprojects { diff --git a/demo/gdml/build.gradle.kts b/demo/gdml/build.gradle.kts index 391db4f9..ead65970 100644 --- a/demo/gdml/build.gradle.kts +++ b/demo/gdml/build.gradle.kts @@ -16,7 +16,7 @@ kotlin { jvm { withJava() } - js{ + js { useCommonJs() browser { commonWebpackConfig { @@ -34,6 +34,7 @@ kotlin { jvmMain { dependencies { implementation(project(":visionforge-fx")) + implementation("ch.qos.logback:logback-classic:1.2.5") } } jsMain { @@ -53,5 +54,5 @@ application { val convertGdmlToJson by tasks.creating(JavaExec::class) { group = "application" classpath = sourceSets["main"].runtimeClasspath - main = "space.kscience.dataforge.vis.spatial.gdml.demo.SaveToJsonKt" + mainClass.set("space.kscience.dataforge.vis.spatial.gdml.demo.SaveToJsonKt") } \ No newline at end of file diff --git a/demo/gdml/src/commonTest/kotlin/space/kscience/visionforge/gdml/GDMLVisionTest.kt b/demo/gdml/src/commonTest/kotlin/space/kscience/visionforge/gdml/GDMLVisionTest.kt index 66332d9d..50ba6d12 100644 --- a/demo/gdml/src/commonTest/kotlin/space/kscience/visionforge/gdml/GDMLVisionTest.kt +++ b/demo/gdml/src/commonTest/kotlin/space/kscience/visionforge/gdml/GDMLVisionTest.kt @@ -1,8 +1,8 @@ package space.kscience.visionforge.gdml -import space.kscience.dataforge.meta.string import space.kscience.dataforge.names.Name import space.kscience.dataforge.values.asValue +import space.kscience.dataforge.values.string import space.kscience.gdml.GdmlShowCase import space.kscience.visionforge.setProperty import space.kscience.visionforge.solid.SolidMaterial @@ -27,6 +27,6 @@ class GDMLVisionTest { val child = vision[Name.of("composite-000","segment-0")] assertNotNull(child) child.setProperty(SolidMaterial.MATERIAL_COLOR_KEY, "red".asValue()) - assertEquals("red", child.getProperty(SolidMaterial.MATERIAL_COLOR_KEY).string) + assertEquals("red", child.getPropertyValue(SolidMaterial.MATERIAL_COLOR_KEY)?.string) } } \ No newline at end of file diff --git a/demo/gdml/src/jvmMain/kotlin/space/kscience/visionforge/gdml/demo/GdmlFxDemoApp.kt b/demo/gdml/src/jvmMain/kotlin/space/kscience/visionforge/gdml/demo/GdmlFxDemoApp.kt index d4cd85b0..cb40431c 100644 --- a/demo/gdml/src/jvmMain/kotlin/space/kscience/visionforge/gdml/demo/GdmlFxDemoApp.kt +++ b/demo/gdml/src/jvmMain/kotlin/space/kscience/visionforge/gdml/demo/GdmlFxDemoApp.kt @@ -7,7 +7,7 @@ import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.fetch import space.kscience.gdml.GdmlShowCase import space.kscience.visionforge.VisionManager -import space.kscience.visionforge.describedProperties +import space.kscience.visionforge.computeProperties import space.kscience.visionforge.editor.VisionEditorFragment import space.kscience.visionforge.editor.VisionTreeFragment import space.kscience.visionforge.gdml.toVision @@ -34,10 +34,10 @@ class GDMLView : View() { } private val propertyEditor = VisionEditorFragment { - it.describedProperties + it.computeProperties() }.apply { descriptorProperty.set(SolidMaterial.descriptor) - itemProperty.bind(treeFragment.selectedProperty) + visionProperty.bind(treeFragment.selectedProperty) } override val root: Parent = borderpane { diff --git a/demo/solid-showcase/src/commonMain/kotlin/space/kscience/visionforge/solid/demo/demo.kt b/demo/solid-showcase/src/commonMain/kotlin/space/kscience/visionforge/solid/demo/demo.kt index fcf94589..c933c3ce 100644 --- a/demo/solid-showcase/src/commonMain/kotlin/space/kscience/visionforge/solid/demo/demo.kt +++ b/demo/solid-showcase/src/commonMain/kotlin/space/kscience/visionforge/solid/demo/demo.kt @@ -1,9 +1,6 @@ package space.kscience.visionforge.solid.demo -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.delay -import kotlinx.coroutines.isActive -import kotlinx.coroutines.launch +import kotlinx.coroutines.* import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.invoke import space.kscience.dataforge.names.Name @@ -76,7 +73,7 @@ fun VisionLayout.showcase() { //override color for this cube color(1530) - launch(Dispatchers.Main) { + GlobalScope.launch(Dispatchers.Main) { while (isActive) { delay(500) visible = !(visible ?: false) @@ -85,7 +82,7 @@ fun VisionLayout.showcase() { } } - launch(Dispatchers.Main) { + GlobalScope.launch(Dispatchers.Main) { val random = Random(111) while (isActive) { delay(1000) diff --git a/demo/solid-showcase/src/jsMain/kotlin/space/kscience/visionforge/solid/demo/VariableBox.kt b/demo/solid-showcase/src/jsMain/kotlin/space/kscience/visionforge/solid/demo/VariableBox.kt index 22cac2c6..af828f46 100644 --- a/demo/solid-showcase/src/jsMain/kotlin/space/kscience/visionforge/solid/demo/VariableBox.kt +++ b/demo/solid-showcase/src/jsMain/kotlin/space/kscience/visionforge/solid/demo/VariableBox.kt @@ -3,6 +3,7 @@ package space.kscience.visionforge.solid.demo import info.laht.threekt.core.Object3D import info.laht.threekt.geometries.BoxGeometry import info.laht.threekt.objects.Mesh +import space.kscience.dataforge.meta.get import space.kscience.dataforge.meta.int import space.kscience.dataforge.meta.number import space.kscience.dataforge.names.asName @@ -43,13 +44,13 @@ internal class VariableBox(val xSize: Number, val ySize: Number) : ThreeJsVision it.layers.enable(this@VariableBox.layer) } } - mesh.scale.z = getOwnProperty(VALUE).number?.toDouble() ?: 1.0 + mesh.scale.z = meta[VALUE].number?.toDouble() ?: 1.0 //add listener to object properties - onPropertyChange(three.context) { name -> + onPropertyChange { name -> when { name == VALUE -> { - val value = getOwnProperty(VALUE).int ?: 0 + val value = meta.get(VALUE).int ?: 0 val size = value.toFloat() / 255f * 20f mesh.scale.z = size.toDouble() mesh.position.z = size.toDouble() / 2 @@ -69,7 +70,7 @@ internal class VariableBox(val xSize: Number, val ySize: Number) : ThreeJsVision } var value: Int - get() = getOwnProperty(VALUE).int ?: 0 + get() = meta[VALUE].int ?: 0 set(value) { setProperty(VALUE, value.asValue()) } diff --git a/ui/bootstrap/build.gradle.kts b/ui/bootstrap/build.gradle.kts index af4ec727..b1b0588e 100644 --- a/ui/bootstrap/build.gradle.kts +++ b/ui/bootstrap/build.gradle.kts @@ -11,5 +11,5 @@ dependencies { implementation(npm("file-saver", "2.0.2")) implementation(npm("bootstrap","4.6.0")) implementation(npm("jquery","3.5.1")) - implementation(npm("@popperjs/core","2.9.3")) + implementation(npm("popper.js","1.16.1")) } \ No newline at end of file diff --git a/ui/bootstrap/src/main/kotlin/space/kscience/visionforge/bootstrap/visionPropertyEditor.kt b/ui/bootstrap/src/main/kotlin/space/kscience/visionforge/bootstrap/visionPropertyEditor.kt index 9c2206f1..65255af6 100644 --- a/ui/bootstrap/src/main/kotlin/space/kscience/visionforge/bootstrap/visionPropertyEditor.kt +++ b/ui/bootstrap/src/main/kotlin/space/kscience/visionforge/bootstrap/visionPropertyEditor.kt @@ -4,13 +4,10 @@ import org.w3c.dom.Element import react.RBuilder import react.dom.render import space.kscience.dataforge.meta.descriptors.MetaDescriptor -import space.kscience.visionforge.Vision -import space.kscience.visionforge.getStyle -import space.kscience.visionforge.meta +import space.kscience.visionforge.* import space.kscience.visionforge.react.metaViewer import space.kscience.visionforge.react.propertyEditor import space.kscience.visionforge.solid.SolidReference -import space.kscience.visionforge.styles public fun RBuilder.visionPropertyEditor( vision: Vision, @@ -20,8 +17,8 @@ public fun RBuilder.visionPropertyEditor( card("Properties") { propertyEditor( - ownProperties = vision.meta(false,false,false), - allProperties = vision.meta(), + ownProperties = vision.meta, + allProperties = vision.computeProperties(), updateFlow = vision.propertyChanges, descriptor = descriptor, key = key diff --git a/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ThreeViewWithControls.kt b/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ThreeViewWithControls.kt index d0aa2ed8..f8439a07 100644 --- a/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ThreeViewWithControls.kt +++ b/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ThreeViewWithControls.kt @@ -14,7 +14,8 @@ import space.kscience.dataforge.names.NameToken import space.kscience.dataforge.names.isEmpty import space.kscience.dataforge.names.length import space.kscience.visionforge.VisionGroup -import space.kscience.visionforge.meta +import space.kscience.visionforge.computeProperties +import space.kscience.visionforge.propertyChanges import space.kscience.visionforge.react.ThreeCanvasComponent import space.kscience.visionforge.react.flexColumn import space.kscience.visionforge.react.flexRow @@ -134,8 +135,8 @@ public val ThreeCanvasWithControls: FunctionComponent + get() { + val metaKeys = vision.meta.items.keys + val descriptorKeys = descriptor?.children?.map { NameToken(it.key) } ?: emptySet() + return (metaKeys + descriptorKeys).associateWith { getMeta(rootName + it) } + } + + override var value: Value? + get() { + val inheritFlag = descriptor?.inherited ?: false + val stylesFlag = descriptor?.usesStyles ?: true + return vision.getPropertyValue(rootName, inheritFlag, stylesFlag, true) + } + set(value) { + vision.meta.setValue(rootName, value) + } + + override fun getMeta(name: Name): ObservableMutableMeta = + ComputedVisionProperties(vision, rootName + name, visionDescriptor) + + override fun getOrCreate(name: Name): ObservableMutableMeta = getMeta(name) + + override fun toMeta(): Meta = this +} + +/** + * Compute property node based on inheritance and style information from the descriptor + */ +public fun Vision.computeProperties(descriptor: MetaDescriptor? = this.descriptor): ObservableMutableMeta = + if (descriptor == null) meta else ComputedVisionProperties(this, Name.EMPTY, descriptor) + +public fun Vision.computePropertyNode(name: Name, descriptor: MetaDescriptor? = this.descriptor): ObservableMutableMeta? = + computeProperties(descriptor)[name] \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/StyleSheet.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/StyleSheet.kt index e2869df8..dacc09b1 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/StyleSheet.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/StyleSheet.kt @@ -5,6 +5,7 @@ import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.NameToken import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.plus +import space.kscience.dataforge.values.Value import space.kscience.dataforge.values.asValue import kotlin.jvm.JvmInline @@ -14,7 +15,7 @@ import kotlin.jvm.JvmInline @JvmInline public value class StyleSheet(private val owner: VisionGroup) { - private val styleNode: Meta? get() = owner.getOwnProperty(STYLESHEET_KEY) + private val styleNode: Meta? get() = owner.meta[STYLESHEET_KEY] public val items: Map? get() = styleNode?.items @@ -24,7 +25,7 @@ public value class StyleSheet(private val owner: VisionGroup) { * Define a style without notifying owner */ public fun define(key: String, style: Meta?) { - owner.setPropertyNode(STYLESHEET_KEY + key, style) + owner.meta.setMeta(STYLESHEET_KEY + key, style) } /** @@ -71,9 +72,9 @@ internal fun Vision.styleChanged(key: String, oldStyle: Meta?, newStyle: Meta?) * List of names of styles applied to this object. Order matters. Not inherited. */ public var Vision.styles: List - get() = getOwnProperty(Vision.STYLE_KEY)?.stringList ?: emptyList() + get() = meta.getMeta(Vision.STYLE_KEY)?.stringList ?: emptyList() set(value) { - setPropertyValue(Vision.STYLE_KEY, value.map { it.asValue() }.asValue()) + meta.setValue(Vision.STYLE_KEY, value.map { it.asValue() }.asValue()) } /** @@ -86,7 +87,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. */ public fun Vision.useStyle(name: String) { - styles = (getOwnProperty(Vision.STYLE_KEY)?.stringList ?: emptyList()) + name + styles = (meta.getMeta(Vision.STYLE_KEY)?.stringList ?: emptyList()) + name } @@ -94,7 +95,12 @@ public fun Vision.useStyle(name: String) { * 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? = - getOwnProperty(StyleSheet.STYLESHEET_KEY + name) ?: parent?.getStyle(name) + meta.getMeta(StyleSheet.STYLESHEET_KEY + name) ?: parent?.getStyle(name) + +/** + * Resolve a property from all styles + */ +public fun Vision.getStyleProperty(name: Name): Value? = styles.firstNotNullOfOrNull { getStyle(it)?.get(name)?.value } /** * Resolve an item in all style layers diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/Vision.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/Vision.kt index daa854a5..e7bdd301 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/Vision.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/Vision.kt @@ -1,27 +1,27 @@ package space.kscience.visionforge -import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.meta.MutableMeta +import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.launch +import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.descriptors.Described import space.kscience.dataforge.meta.descriptors.MetaDescriptor -import space.kscience.dataforge.meta.update +import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.misc.Type import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.asName import space.kscience.dataforge.values.Value +import space.kscience.dataforge.values.asValue +import space.kscience.dataforge.values.boolean import space.kscience.visionforge.Vision.Companion.TYPE -import kotlin.coroutines.CoroutineContext -import kotlin.coroutines.EmptyCoroutineContext /** * A root type for display hierarchy */ @Type(TYPE) -public interface Vision : Described, CoroutineScope { +public interface Vision : Described, Configurable { /** * The parent object of this one. If null, this one is a root. @@ -33,47 +33,23 @@ public interface Vision : Described, CoroutineScope { */ public val manager: VisionManager? get() = parent?.manager - override val coroutineContext: CoroutineContext - get() = manager?.context?.coroutineContext ?: EmptyCoroutineContext + /** + * This Vision own properties (ignoring inheritance, styles and defaults + */ + override val meta: ObservableMutableMeta /** - * Get property. + * Get property value with given layer flags. * @param inherit toggles parent node property lookup. Null means inference from descriptor. Default is false. - * @param includeStyles toggles inclusion of. Null means inference from descriptor. Default is true. + * @param includeStyles toggles inclusion of properties from styles. default is true */ - public fun getProperty( + public fun getPropertyValue( name: Name, inherit: Boolean = false, includeStyles: Boolean = true, includeDefaults: Boolean = true, - ): Meta? + ): Value? - /** - * 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`. - */ - public fun getOwnProperty(name: Name): Meta? = getProperty( - name, - inherit = false, - includeStyles = false, - includeDefaults = false - ) - - /** - * 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) - - /** - * Flow of property invalidation events. It does not contain property values after invalidation since it is not clear - * if it should include inherited properties etc. - */ - public val propertyChanges: Flow /** * Notify all listeners that a property has been changed and should be invalidated @@ -83,7 +59,7 @@ public interface Vision : Described, CoroutineScope { /** * Update this vision using a dif represented by [VisionChange]. */ - public fun change(change: VisionChange) + public fun update(change: VisionChange) override val descriptor: MetaDescriptor? @@ -96,58 +72,60 @@ public interface Vision : Described, CoroutineScope { } /** - * Subscribe on property updates. The subscription is bound to the given [scope] and canceled when the scope is canceled + * Flow of property invalidation events. It does not contain property values after invalidation since it is not clear + * if it should include inherited properties etc. */ -public fun Vision.onPropertyChange(scope: CoroutineScope, callback: suspend (Name) -> Unit) { - propertyChanges.onEach(callback).launchIn(scope) -} - +@OptIn(ExperimentalCoroutinesApi::class) +@DFExperimental +public val Vision.propertyChanges: Flow + get() = callbackFlow { + meta.onChange(this) { name -> + launch { + send(name) + } + } + awaitClose { + meta.removeListener(this) + } + } /** - * Own properties, excluding inheritance, styles and descriptor + * Subscribe on property updates. The subscription is bound to the given [scope] and canceled when the scope is canceled */ -public fun Vision.meta( - inherit: Boolean = false, - includeStyles: Boolean = true, - includeDefaults: Boolean = true, -): MutableMeta = VisionProperties(this, Name.EMPTY, inherit, includeStyles, includeDefaults) - -public fun Vision.configure(target: Name = Name.EMPTY, block: MutableMeta.() -> Unit): Unit { - VisionProperties(this, target).apply(block) +public fun Vision.onPropertyChange(callback: Meta.(Name) -> Unit) { + meta.onChange(null, callback) } -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 */ -public fun Vision.getProperty( +public fun Vision.getPropertyValue( key: String, inherit: Boolean = false, includeStyles: Boolean = true, includeDefaults: Boolean = true, -): Meta? = getProperty(Name.parse(key), inherit, includeStyles, includeDefaults) - +): Value? = getPropertyValue(Name.parse(key), inherit, includeStyles, includeDefaults) /** * A convenience method to set property node or value. If Item is null, then node is removed, not a value */ public fun Vision.setProperty(name: Name, item: Any?) { when (item) { - null -> setPropertyNode(name, null) - is Meta -> setPropertyNode(name, item) - is Value -> setPropertyValue(name, item) + null -> meta.remove(name) + is Meta -> meta.setMeta(name, item) + is Value -> meta.setValue(name, item) + else -> meta.setValue(name, Value.of(item)) } } public fun Vision.setPropertyNode(key: String, item: Any?) { setProperty(Name.parse(key), item) } + +/** + * Control visibility of the element + */ +public var Vision.visible: Boolean? + get() = getPropertyValue(Vision.VISIBLE_KEY)?.boolean + set(value) = meta.setValue(Vision.VISIBLE_KEY, value?.asValue()) + diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionBase.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionBase.kt index 46e5f62a..c123938d 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionBase.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionBase.kt @@ -1,24 +1,27 @@ package space.kscience.visionforge -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.launch import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.Transient -import space.kscience.dataforge.meta.* +import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.MutableMeta +import space.kscience.dataforge.meta.ObservableMutableMeta 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.meta.get import space.kscience.dataforge.misc.DFExperimental -import space.kscience.dataforge.names.Name -import space.kscience.dataforge.names.asName -import space.kscience.dataforge.names.plus +import space.kscience.dataforge.names.* import space.kscience.dataforge.values.Value import space.kscience.dataforge.values.ValueType import space.kscience.visionforge.Vision.Companion.STYLE_KEY import kotlin.jvm.Synchronized +internal data class MetaListener( + val owner: Any? = null, + val callback: Meta.(name: Name) -> Unit, +) + /** * A full base implementation for a [Vision] * @param properties Object own properties excluding styles and inheritance @@ -27,12 +30,9 @@ import kotlin.jvm.Synchronized @SerialName("vision") public open class VisionBase( @Transient override var parent: VisionGroup? = null, - @Serializable(MutableMetaSerializer::class) protected var properties: MutableMeta? = null ) : Vision { - //protected val observableProperties: ObservableMutableMeta by lazy { properties.asObservable() } - @Synchronized protected fun getOrCreateProperties(): MutableMeta { if (properties == null) { @@ -42,82 +42,105 @@ public open class VisionBase( return properties!! } - /** - * A fast accessor method to get own property (no inheritance or styles - */ - override fun getOwnProperty(name: Name): Meta? = properties?.getMeta(name) + @Transient + private val listeners = HashSet() - override fun getProperty( + private inner class VisionBaseProperties(val rootName: Name) : ObservableMutableMeta { + + override val items: Map + get() = properties?.get(rootName)?.items?.mapValues { entry -> + VisionBaseProperties(rootName + entry.key) + } ?: emptyMap() + + override var value: Value? + get() = properties?.get(rootName)?.value + set(value) { + getOrCreateProperties().setValue(rootName, value) + } + + override fun getOrCreate(name: Name): ObservableMutableMeta = VisionBaseProperties(this.rootName + name) + + override fun setMeta(name: Name, node: Meta?) { + getOrCreateProperties().setMeta(name, node) + } + + @DFExperimental + override fun attach(name: Name, node: ObservableMutableMeta) { + val ownProperties = getOrCreateProperties() + if (ownProperties is ObservableMutableMeta) { + ownProperties.attach(rootName + name, node) + } else { + ownProperties.setMeta(rootName + name, node) + node.onChange(this) { childName -> + ownProperties.setMeta(rootName + name + childName, this[childName]) + } + } + } + + override fun invalidate(name: Name) { + invalidateProperty(rootName + name) + } + + @Synchronized + override fun onChange(owner: Any?, callback: Meta.(name: Name) -> Unit) { + if (rootName.isEmpty()) { + listeners.add((MetaListener(owner, callback))) + } else { + listeners.add(MetaListener(owner) { name -> + if (name.startsWith(rootName)) { + (get(rootName) ?: Meta.EMPTY).callback(name.removeHeadOrNull(rootName)!!) + } + }) + } + } + + @Synchronized + override fun removeListener(owner: Any?) { + listeners.removeAll { it.owner === owner } + } + + override fun toString(): String = Meta.toString(this) + override fun equals(other: Any?): Boolean = Meta.equals(this, other as? Meta) + override fun hashCode(): Int = Meta.hashCode(this) + } + + override val meta: ObservableMutableMeta get() = VisionBaseProperties(Name.EMPTY) + + override fun getPropertyValue( name: Name, inherit: Boolean, includeStyles: Boolean, includeDefaults: Boolean, - ): Meta? = if (!inherit && !includeStyles && !includeDefaults) { - getOwnProperty(name) - } else { - buildList { - add(getOwnProperty(name)) - if (includeStyles) { - addAll(getStyleItems(name)) - } - if (inherit) { - add(parent?.getProperty(name, inherit, includeStyles, includeDefaults)) - } - if (includeDefaults) { - add(descriptor?.defaultNode?.get(name)) - } - }.merge() - } - - override fun setPropertyNode(name: Name, node: Meta?, notify: Boolean) { - val oldItem = properties?.get(name) - if (oldItem != node) { - getOrCreateProperties().setMeta(name, node) - if (notify) { - invalidateProperty(name) - } + ): Value? { + properties?.get(name)?.value?.let { return it } + if (includeStyles) { + getStyleProperty(name)?.let { return it } } - } - - 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) - } + if (inherit) { + parent?.getPropertyValue(name, inherit, includeStyles, includeDefaults)?.let { return it } } + if (includeDefaults) { + descriptor?.defaultNode?.get(name)?.value.let { return it } + } + return null } override val descriptor: MetaDescriptor? get() = null - private suspend fun updateStyles(names: List) { - names.mapNotNull { getStyle(it) }.asSequence() - .flatMap { it.items.asSequence() } - .distinctBy { it.key } - .forEach { - invalidateProperty(it.key.asName()) - } - } - - //TODO check memory consumption for the flow - @Transient - private val propertyInvalidationFlow: MutableSharedFlow = MutableSharedFlow() - - @DFExperimental - override val propertyChanges: Flow - get() = propertyInvalidationFlow override fun invalidateProperty(propertyName: Name) { - launch { - if (propertyName == STYLE_KEY) { - updateStyles(styles) - } - propertyInvalidationFlow.emit(propertyName) + if (propertyName == STYLE_KEY) { + styles.mapNotNull { getStyle(it) }.asSequence() + .flatMap { it.items.asSequence() } + .distinctBy { it.key } + .forEach { + invalidateProperty(it.key.asName()) + } } + listeners.forEach { it.callback(properties ?: Meta.EMPTY, propertyName) } } - override fun change(change: VisionChange) { + override fun update(change: VisionChange) { change.properties?.let { updateProperties(Name.EMPTY, it) } @@ -131,7 +154,7 @@ public open class VisionBase( } public fun Vision.updateProperties(at: Name, item: Meta) { - setPropertyValue(at, item.value) + meta.setValue(at, item.value) item.items.forEach { (token, item) -> updateProperties(at + token, item) } diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionChange.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionChange.kt index e538a89c..0275934b 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionChange.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionChange.kt @@ -89,8 +89,8 @@ private fun CoroutineScope.collectChange( ) { //Collect properties change - source.onPropertyChange(this) { propertyName -> - val newItem = source.getOwnProperty(propertyName) + source.onPropertyChange { propertyName -> + val newItem = source.meta[propertyName] collector().propertyChanged(name, propertyName, newItem) } diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroupBase.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroupBase.kt index aab27bc5..f4d25b08 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroupBase.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroupBase.kt @@ -1,5 +1,6 @@ package space.kscience.visionforge +import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.launch @@ -8,7 +9,6 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.Transient import space.kscience.dataforge.names.* - /** * Abstract implementation of mutable group of [Vision] * @@ -48,7 +48,7 @@ public open class VisionGroupBase( * Propagate children change event upwards */ private fun childrenChanged(name: NameToken, before: Vision?, after: Vision?) { - launch { + (manager?.context?: GlobalScope).launch { _structureChanges.emit(MutableVisionGroup.StructureChange(name, before, after)) } } @@ -131,15 +131,15 @@ public open class VisionGroupBase( } } - override fun change(change: VisionChange) { + override fun update(change: VisionChange) { change.children?.forEach { (name, change) -> when { change.delete -> set(name, null) change.vision != null -> set(name, change.vision) - else -> get(name)?.change(change) + else -> get(name)?.update(change) } } - super.change(change) + super.update(change) } } @@ -151,6 +151,6 @@ internal class RootVisionGroup(override val manager: VisionManager) : VisionGrou /** * Designate this [VisionGroup] as a root group and assign a [VisionManager] as its parent */ -public fun Vision.root(manager: VisionManager){ +public fun Vision.root(manager: VisionManager) { parent = RootVisionGroup(manager) } \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionProperties.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionProperties.kt deleted file mode 100644 index 363ad729..00000000 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionProperties.kt +++ /dev/null @@ -1,38 +0,0 @@ -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 - 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) -} \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionPropertyContainer.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionPropertyContainer.kt index f6f822b7..fed474fc 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionPropertyContainer.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionPropertyContainer.kt @@ -1,7 +1,7 @@ package space.kscience.visionforge import space.kscience.dataforge.meta.Configurable -import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.MutableMeta import space.kscience.dataforge.meta.ObservableMutableMeta import space.kscience.dataforge.meta.get import space.kscience.dataforge.names.Name @@ -11,39 +11,24 @@ import space.kscience.dataforge.values.Value * Property containers are used to create a symmetric behaviors for vision properties and style builders */ public interface VisionPropertyContainer { - public fun getProperty( + + public val meta: MutableMeta + + public fun getPropertyValue( name: Name, inherit: Boolean = false, includeStyles: Boolean = true, includeDefaults: Boolean = true, - ): Meta? - - /** - * 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) + ): Value? } public open class SimpleVisionPropertyContainer( override val meta: ObservableMutableMeta, ) : VisionPropertyContainer, Configurable { - override fun getProperty( + override fun getPropertyValue( name: Name, inherit: Boolean, includeStyles: Boolean, includeDefaults: Boolean - ): Meta? = meta[name] - - override fun setPropertyNode(name: Name, node: Meta?, notify: Boolean) { - this.meta.setMeta(name, node) - } - - override fun setPropertyValue(name: Name, value: Value?, notify: Boolean) { - this.meta.setValue(name, value) - } + ): Value? = meta[name]?.value } \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/misc.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/misc.kt index 900f6f1c..084416ac 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/misc.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/misc.kt @@ -2,9 +2,7 @@ package space.kscience.visionforge 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 @DslMarker public annotation class VisionBuilder @@ -16,11 +14,4 @@ public fun List.merge(): Meta? { first.isLeaf -> first //fast search for first entry if it is value else -> Laminate(filterNotNull()) //merge nodes if first encountered node is meta } -} - -/** - * Control visibility of the element - */ -public var Vision.visible: Boolean? - get() = getProperty(Vision.VISIBLE_KEY).boolean - set(value) = setPropertyValue(Vision.VISIBLE_KEY, value?.asValue()) \ No newline at end of file +} \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/schemeDesctiptors.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/schemeDesctiptors.kt deleted file mode 100644 index 403312ea..00000000 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/schemeDesctiptors.kt +++ /dev/null @@ -1,66 +0,0 @@ -package space.kscience.visionforge - -import space.kscience.dataforge.meta.Scheme -import space.kscience.dataforge.meta.SchemeSpec -import space.kscience.dataforge.meta.descriptors.Described -import space.kscience.dataforge.meta.descriptors.MetaDescriptorBuilder -import space.kscience.dataforge.meta.descriptors.item -import space.kscience.dataforge.meta.descriptors.value -import space.kscience.dataforge.names.Name -import space.kscience.dataforge.values.ValueType -import kotlin.reflect.KProperty1 -import kotlin.reflect.typeOf - -/** - * TODO to be moved into the core - */ -public inline fun MetaDescriptorBuilder.value( - property: KProperty1, - noinline block: MetaDescriptorBuilder.() -> Unit = {}, -) { - when (typeOf()) { - typeOf(), typeOf(), typeOf(), typeOf(), typeOf(), typeOf() -> - value(property.name, ValueType.NUMBER) { - block() - } - typeOf(), typeOf(), typeOf(), typeOf(), typeOf(), typeOf() -> - value(property.name, ValueType.NUMBER) { - block() - } - typeOf() -> value(property.name, ValueType.BOOLEAN) { - block() - } - typeOf>(), typeOf>(), typeOf>(), typeOf>(), typeOf>(), typeOf>(), - typeOf(), typeOf(), typeOf(), typeOf(), typeOf(), - -> value(property.name, ValueType.NUMBER) { - multiple = true - block() - } - typeOf() -> value(property.name, ValueType.STRING) { - block() - } - typeOf>(), typeOf>() -> value(property.name, ValueType.STRING) { - multiple = true - block() - } - else -> item(property.name, block) - } -} - -public fun MetaDescriptorBuilder.item( - key: String, - described: Described, - block: MetaDescriptorBuilder.() -> Unit = {}, -) { - described.descriptor?.let { - item(Name.parse(key), it, block) - } -} - -public inline fun MetaDescriptorBuilder.scheme( - property: KProperty1, - spec: SchemeSpec, - noinline block: MetaDescriptorBuilder.() -> Unit = {}, -) { - item(property.name, spec, block) -} diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/visionDelegates.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/visionDelegates.kt index 524363d7..999bbb45 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/visionDelegates.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/visionDelegates.kt @@ -1,45 +1,43 @@ 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 = object : ReadWriteProperty { - 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 Vision.propertyNode( - converter: MetaConverter, - name: Name? = null, - inherit: Boolean = false, - includeStyles: Boolean = true, - includeDefaults: Boolean = true, -): ReadWriteProperty = object : ReadWriteProperty { - 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.propertyNode( +// name: Name? = null, +// inherit: Boolean = false, +// includeStyles: Boolean = true, +// includeDefaults: Boolean = true, +//): ReadWriteProperty = object : ReadWriteProperty { +// 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?) { +// meta.setMeta(name ?: Name.parse(property.name), value) +// } +//} +// +//public fun Vision.propertyNode( +// converter: MetaConverter, +// name: Name? = null, +// inherit: Boolean = false, +// includeStyles: Boolean = true, +// includeDefaults: Boolean = true, +//): ReadWriteProperty = object : ReadWriteProperty { +// 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?) { +// meta.setMeta(name ?: Name.parse(property.name), value?.let(converter::objectToMeta)) +// } +//} public fun Vision.propertyValue( name: Name? = null, @@ -48,10 +46,10 @@ public fun Vision.propertyValue( includeDefaults: Boolean = true, ): ReadWriteProperty = object : ReadWriteProperty { override fun getValue(thisRef: Any?, property: KProperty<*>): Value? = - getProperty(name ?: Name.parse(property.name), inherit, includeStyles, includeDefaults)?.value + getPropertyValue(name ?: Name.parse(property.name), inherit, includeStyles, includeDefaults) override fun setValue(thisRef: Any?, property: KProperty<*>, value: Value?) { - setPropertyValue(name ?: Name.parse(property.name), value) + meta.setValue(name ?: Name.parse(property.name), value) } } @@ -63,15 +61,15 @@ public fun Vision.propertyValue( setter: (T) -> Value? = { it?.let(Value::of) }, getter: (Value?) -> T, ): ReadWriteProperty = object : ReadWriteProperty { - override fun getValue(thisRef: Any?, property: KProperty<*>): T = getProperty( + override fun getValue(thisRef: Any?, property: KProperty<*>): T = getPropertyValue( name ?: Name.parse(property.name), inherit, includeStyles, includeDefaults - )?.value.let(getter) + ).let(getter) override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { - setPropertyValue(name ?: Name.parse(property.name), value?.let(setter)) + meta.setValue(name ?: Name.parse(property.name), value?.let(setter)) } } diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/visionDescriptor.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/visionDescriptor.kt index de505c50..a02a3b3e 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/visionDescriptor.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/visionDescriptor.kt @@ -2,7 +2,6 @@ package space.kscience.visionforge import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.descriptors.* -import space.kscience.dataforge.names.asName import space.kscience.dataforge.values.asValue private const val INHERITED_DESCRIPTOR_ATTRIBUTE = "inherited" @@ -23,14 +22,6 @@ public var MetaDescriptorBuilder.usesStyles: Boolean get() = attributes[STYLE_DESCRIPTOR_ATTRIBUTE].boolean ?: true set(value) = attributes.set(STYLE_DESCRIPTOR_ATTRIBUTE, value) - -public val Vision.describedProperties: Meta - get() = Meta { - descriptor?.children?.forEach { (key, descriptor) -> - this.setMeta(key.asName(), getProperty(key, inherit = descriptor.inherited)) - } - } - public val MetaDescriptor.widget: Meta get() = attributes["widget"] ?: Meta.EMPTY diff --git a/visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/html/HtmlTagTest.kt b/visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/html/HtmlTagTest.kt index 2a7c6d28..c09c7c6c 100644 --- a/visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/html/HtmlTagTest.kt +++ b/visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/html/HtmlTagTest.kt @@ -5,10 +5,17 @@ import kotlinx.html.stream.createHTML import space.kscience.dataforge.context.Global import space.kscience.dataforge.context.fetch import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.configure import space.kscience.dataforge.meta.set import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.names.Name -import space.kscience.visionforge.* +import space.kscience.visionforge.Vision +import space.kscience.visionforge.VisionBase +import space.kscience.visionforge.VisionManager +import kotlin.collections.HashMap +import kotlin.collections.Map +import kotlin.collections.forEach +import kotlin.collections.set import kotlin.test.Test typealias HtmlVisionRenderer = FlowContent.(name: Name, vision: Vision, meta: Meta) -> Unit @@ -57,7 +64,7 @@ class HtmlTagTest { div { h2 { +"Properties" } ul { - (vision as? VisionBase)?.meta()?.items?.forEach { + (vision as? VisionBase)?.meta?.items?.forEach { li { a { +it.key.toString() } p { +it.value.toString() } diff --git a/visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/meta/VisionPropertyTest.kt b/visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/meta/VisionPropertyTest.kt new file mode 100644 index 00000000..40b4c96e --- /dev/null +++ b/visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/meta/VisionPropertyTest.kt @@ -0,0 +1,44 @@ +package space.kscience.visionforge.meta + +import space.kscience.dataforge.meta.* +import space.kscience.dataforge.values.asValue +import space.kscience.visionforge.VisionBase +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotEquals + +class VisionPropertyTest { + @Test + fun testPropertyWrite(){ + val vision = VisionBase() + vision.meta["fff"] = 2 + vision.meta["fff.ddd"] = false + + assertEquals(2, vision.meta["fff"]?.int) + assertEquals(false, vision.meta["fff.ddd"]?.boolean) + } + + @Test + fun testPropertyEdit(){ + val vision = VisionBase() + vision.meta.getOrCreate("fff.ddd").apply { + value = 2.asValue() + } + assertEquals(2, vision.meta["fff.ddd"]?.int) + assertNotEquals(true, vision.meta["fff.ddd"]?.boolean) + } + + internal class TestScheme: Scheme(){ + var ddd by int() + companion object: SchemeSpec(::TestScheme) + } + + @Test + fun testPropertyUpdate(){ + val vision = VisionBase() + vision.meta.getOrCreate("fff").updateWith(TestScheme){ + ddd = 2 + } + assertEquals(2, vision.meta["fff.ddd"]?.int) + } +} \ No newline at end of file diff --git a/visionforge-core/src/jsMain/kotlin/space/kscience/visionforge/VisionClient.kt b/visionforge-core/src/jsMain/kotlin/space/kscience/visionforge/VisionClient.kt index ae691d4b..017c615d 100644 --- a/visionforge-core/src/jsMain/kotlin/space/kscience/visionforge/VisionClient.kt +++ b/visionforge-core/src/jsMain/kotlin/space/kscience/visionforge/VisionClient.kt @@ -92,7 +92,7 @@ public class VisionClient : AbstractPlugin() { } logger.debug { "Got update $change for output with name $name" } - vision.change(change) + vision.update(change) } else { console.error("WebSocket message data is not a string") } diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/FXMetaModel.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/FXMetaModel.kt index a3509221..476890a6 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/FXMetaModel.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/FXMetaModel.kt @@ -3,11 +3,14 @@ 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.beans.binding.ObjectBinding import javafx.collections.ObservableList -import space.kscience.dataforge.meta.* +import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.ObservableMeta +import space.kscience.dataforge.meta.boolean import space.kscience.dataforge.meta.descriptors.MetaDescriptor import space.kscience.dataforge.meta.descriptors.get +import space.kscience.dataforge.meta.get import space.kscience.dataforge.names.* import space.kscience.dataforge.values.Value import tornadofx.* @@ -22,11 +25,13 @@ public class FXMetaModel( public val title: String = nodeName.lastOrNull()?.toString() ?: "Meta" ) : Comparable> { - private val existingNode = SimpleObjectProperty(root[nodeName]) + private val existingNode = object: ObjectBinding() { + override fun computeValue(): Meta? =root[nodeName] + } public val children: ListBinding> = object : ListBinding>() { override fun computeValue(): ObservableList> { - val nodeKeys = existingNode.get().items.keys + val nodeKeys = existingNode.get()?.items?.keys?: emptySet() val descriptorKeys = descriptor?.children?.keys?.map { NameToken(it) } ?: emptySet() return (nodeKeys + descriptorKeys).map { FXMetaModel( @@ -43,7 +48,7 @@ public class FXMetaModel( if (root is ObservableMeta) { root.onChange(this) { changed -> if (changed.startsWith(nodeName)) { - if (nodeName.length == changed.length) existingNode.set(root[nodeName]) + if (nodeName.length == changed.length) existingNode.invalidate() else if (changed.length == nodeName.length + 1) children.invalidate() } } @@ -57,7 +62,7 @@ public class FXMetaModel( public val exists: Boolean by existsProperty public val valueProperty: Binding = existingNode.objectBinding { - existingNode.get().value ?: descriptor?.defaultValue + existingNode.get()?.value ?: descriptor?.defaultValue } override fun compareTo(other: FXMetaModel<*>): Int = if (this.exists == other.exists) { @@ -77,141 +82,4 @@ public class FXMetaModel( rootName: String = "root" ): FXMetaModel = 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 = objectBinding(innerDescriptorProperty) { -// value ?: parent?.descriptor?.get(this@FXMeta.name.body) -// } -// -// val descriptor: MetaDescriptor? by descriptorProperty -// -// private val innerNodeProperty = SimpleObjectProperty(nodeValue) -// -// val nodeProperty: ObjectBinding = objectBinding(innerNodeProperty) { -// value ?: parent?.node?.get(this@FXMeta.name) -// } -// -// val node by nodeProperty -// -// val hasValue: ObservableBooleanValue = nodeProperty.booleanBinding { it != null } -// -// private val filter: (FXMeta) -> Boolean = { cfg -> -// !(cfg.descriptor?.attributes?.get(MutableMetaEditor.NO_CONFIGURATOR_TAG)?.boolean ?: false) -// } -// -// val children: ListBinding> = object : ListBinding>() { -// -// 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> { -// val nodeKeys = node?.items?.keys?.toSet() ?: emptySet() -// val descriptorKeys = descriptor?.children?.keys?.map { NameToken(it) } ?: emptyList() -// val keys: Set = 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 FXMeta.remove(name: NameToken) { -// node?.remove(name.asName()) -// children.invalidate() -//} -// -//private fun 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 FXMeta.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 FXMetaModel.remove() { - root.remove(nodeName) -} - -// -//internal fun FXMeta.addValue(key: String) { -// val parent = getOrCreateNode() -// if (descriptor?.multiple == true) { -// parent.append(key, Null) -// } else { -// parent[key] = Null -// } -//} -// -//internal fun FXMeta.addNode(key: String) { -// val parent = getOrCreateNode() -// if (descriptor?.multiple == true) { -// parent.append(key, Meta.EMPTY) -// } else { -// parent[key] = Meta.EMPTY -// } -//} -// -internal fun FXMetaModel.setValue(value: Value?) { - root.setValue(nodeName, value) } \ No newline at end of file diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/MutableMetaEditor.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/MutableMetaEditor.kt index c7238b44..59f49c38 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/MutableMetaEditor.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/MutableMetaEditor.kt @@ -5,6 +5,7 @@ */ package space.kscience.visionforge.editor +import javafx.beans.property.SimpleStringProperty import javafx.scene.control.* import javafx.scene.control.cell.TextFieldTreeTableCell import javafx.scene.layout.BorderPane @@ -13,6 +14,7 @@ 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.dataforge.meta.remove import space.kscience.visionforge.dfIconView import tornadofx.* @@ -32,8 +34,7 @@ public class MutableMetaEditor( MutableMeta: MutableMeta, descriptor: MetaDescriptor?, title: String = "Configuration editor" - ) : - this(FXMetaModel.root(MutableMeta, descriptor = descriptor), title = title) + ) : this(FXMetaModel.root(MutableMeta, descriptor = descriptor), title = title) override val root: BorderPane = borderpane { center = treetableview> { @@ -64,7 +65,7 @@ public class MutableMetaEditor( contextmenu { item("Remove") { action { - content.remove() + content.root.remove(content.nodeName) } } } @@ -83,7 +84,7 @@ public class MutableMetaEditor( } column("Description") { param: TreeTableColumn.CellDataFeatures, String> -> - (param.value.value.descriptor?.info ?: "").observable() + SimpleStringProperty(param.value.value.descriptor?.info ?: "") }.setCellFactory { param: TreeTableColumn, String> -> val cell = TreeTableCell, String>() val text = Text() @@ -126,51 +127,10 @@ public class MutableMetaEditor( Global, item.valueProperty, item.descriptor - ) { - item.setValue(it) + ) { value -> + item.root.setValue(item.nodeName, value) } graphic = chooser.node -// when (item) { -// is FXMetaValue -> { -// text = null -// val chooser = ValueChooser.build( -// Global, -// item.valueProperty, -// item.descriptor -// ) { -// item.set(it) -// } -// graphic = chooser.node -// } -// is FXMetaNode -> { -// 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 diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/VisionEditorFragment.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/VisionEditorFragment.kt index 3ce3b2de..d159dd11 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/VisionEditorFragment.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/VisionEditorFragment.kt @@ -5,41 +5,22 @@ import javafx.beans.property.SimpleObjectProperty import javafx.scene.Node import javafx.scene.Parent import javafx.scene.layout.VBox -import space.kscience.dataforge.meta.* +import space.kscience.dataforge.meta.ObservableMutableMeta import space.kscience.dataforge.meta.descriptors.MetaDescriptor -import space.kscience.visionforge.* +import space.kscience.visionforge.Vision +import space.kscience.visionforge.computeProperties +import space.kscience.visionforge.getStyle +import space.kscience.visionforge.styles import tornadofx.* -public class VisionEditorFragment(public val selector: (Vision) -> Meta) : Fragment() { +public class VisionEditorFragment(public val selector: (Vision) -> ObservableMutableMeta = {it.computeProperties()}) : Fragment() { - public val itemProperty: SimpleObjectProperty = SimpleObjectProperty() - public var item: Vision? by itemProperty + public val visionProperty: SimpleObjectProperty = SimpleObjectProperty() + public var vision: Vision? by visionProperty public val descriptorProperty: SimpleObjectProperty = SimpleObjectProperty() - public constructor( - item: Vision?, - descriptor: MetaDescriptor?, - selector: (Vision) -> MutableMetaProvider = { it.meta() }, - ) : this({ it.describedProperties }) { - this.item = item - this.descriptorProperty.set(descriptor) - } - - private var currentConfig: ObservableMutableMeta? = null - - private val configProperty: Binding = itemProperty.objectBinding { vision -> - if (vision == null) return@objectBinding null - val meta = selector(vision) - val config = MutableMeta { - update(meta) - } - config.onChange(this@VisionEditorFragment) { key -> - vision.setPropertyNode(key, config[key]) - } - //remember old config reference to cleanup listeners - currentConfig?.removeListener(this) - currentConfig = config - config + private val configProperty: Binding = visionProperty.objectBinding { vision -> + vision?.let(selector) } private val configEditorProperty: Binding = configProperty.objectBinding(descriptorProperty) { @@ -50,8 +31,8 @@ public class VisionEditorFragment(public val selector: (Vision) -> Meta) : Fragm private val styleBoxProperty: Binding = configProperty.objectBinding() { VBox().apply { - item?.styles?.forEach { styleName -> - val styleMeta = item?.getStyle(styleName) + vision?.styles?.forEach { styleName -> + val styleMeta = vision?.getStyle(styleName) if (styleMeta != null) { titledpane(styleName, node = MetaViewer(styleMeta).root) } @@ -64,7 +45,7 @@ public class VisionEditorFragment(public val selector: (Vision) -> Meta) : Fragm contentProperty().bind(configEditorProperty) } titledpane("Styles", collapsible = false) { - visibleWhen(itemProperty.booleanBinding { it?.styles?.isNotEmpty() ?: false }) + visibleWhen(visionProperty.booleanBinding { it?.styles?.isNotEmpty() ?: false }) contentProperty().bind(styleBoxProperty) } } diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FX3DPlugin.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FX3DPlugin.kt index f843e02c..72a7ac8a 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FX3DPlugin.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FX3DPlugin.kt @@ -16,6 +16,7 @@ import space.kscience.dataforge.context.* import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.boolean import space.kscience.dataforge.misc.Type +import space.kscience.visionforge.computePropertyNode import space.kscience.visionforge.solid.FX3DFactory.Companion.TYPE import space.kscience.visionforge.solid.SolidMaterial.Companion.MATERIAL_KEY import space.kscience.visionforge.solid.SolidMaterial.Companion.MATERIAL_WIREFRAME_KEY @@ -73,7 +74,7 @@ class FX3DPlugin : AbstractPlugin() { is PolyLine -> PolyLine3D( obj.points.map { Point3D(it.x, it.y, it.z) }, obj.thickness.toFloat(), - obj.getProperty(SolidMaterial.MATERIAL_COLOR_KEY, inherit = true)?.color() + obj.computePropertyNode(SolidMaterial.MATERIAL_COLOR_KEY)?.color() ).apply { this.meshView.cullFace = CullFace.FRONT } diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXReferenceFactory.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXReferenceFactory.kt index d2863cea..ac05e8d5 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXReferenceFactory.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXReferenceFactory.kt @@ -17,7 +17,7 @@ class FXReferenceFactory(val plugin: FX3DPlugin) : FX3DFactory + obj.onPropertyChange { name-> if (name.firstOrNull()?.body == SolidReferenceGroup.REFERENCE_CHILD_PROPERTY_PREFIX) { val childName = name.firstOrNull()?.index?.let(Name::parse) ?: error("Wrong syntax for reference child property: '$name'") val propertyName = name.cutFirst() diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/VisualObjectFXBinding.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/VisualObjectFXBinding.kt index 492356ea..607913d7 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/VisualObjectFXBinding.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/VisualObjectFXBinding.kt @@ -7,6 +7,7 @@ import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.startsWith import space.kscience.dataforge.values.Value import space.kscience.visionforge.Vision +import space.kscience.visionforge.computePropertyNode import space.kscience.visionforge.onPropertyChange import tornadofx.* @@ -17,7 +18,7 @@ public class VisualObjectFXBinding(public val fx: FX3DPlugin, public val obj: Vi private val bindings = HashMap>() init { - obj.onPropertyChange(fx.context) { name -> + obj.onPropertyChange { name -> bindings.filter { it.key.startsWith(name) }.forEach { entry -> Platform.runLater { entry.value.invalidate() @@ -35,7 +36,7 @@ public class VisualObjectFXBinding(public val fx: FX3DPlugin, public val obj: Vi public operator fun get(key: Name): ObjectBinding { return bindings.getOrPut(key) { object : ObjectBinding() { - override fun computeValue(): Meta? = obj.getProperty(key) + override fun computeValue(): Meta? = obj.computePropertyNode(key) } } } diff --git a/visionforge-markdown/src/commonMain/kotlin/space/kscience/visionforge/markup/VisionOfMarkup.kt b/visionforge-markdown/src/commonMain/kotlin/space/kscience/visionforge/markup/VisionOfMarkup.kt index 2103078d..fd1dcd80 100644 --- a/visionforge-markdown/src/commonMain/kotlin/space/kscience/visionforge/markup/VisionOfMarkup.kt +++ b/visionforge-markdown/src/commonMain/kotlin/space/kscience/visionforge/markup/VisionOfMarkup.kt @@ -21,7 +21,7 @@ public class VisionOfMarkup( //TODO add templates public var content: String? - get() = getOwnProperty(CONTENT_PROPERTY_KEY).string + get() = meta.getMeta(CONTENT_PROPERTY_KEY).string set(value) { setProperty(CONTENT_PROPERTY_KEY, value) } diff --git a/visionforge-plotly/src/commonMain/kotlin/space/kscience/visionforge/plotly/VisionOfPlotly.kt b/visionforge-plotly/src/commonMain/kotlin/space/kscience/visionforge/plotly/VisionOfPlotly.kt index 7b2f1c25..36102036 100644 --- a/visionforge-plotly/src/commonMain/kotlin/space/kscience/visionforge/plotly/VisionOfPlotly.kt +++ b/visionforge-plotly/src/commonMain/kotlin/space/kscience/visionforge/plotly/VisionOfPlotly.kt @@ -2,8 +2,6 @@ package space.kscience.visionforge.plotly import kotlinx.serialization.SerialName 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.plotly.Plot import space.kscience.plotly.Plotly @@ -18,7 +16,7 @@ public class VisionOfPlotly private constructor() : VisionBase() { properties = plot.meta } - public val plot: Plot get() = Plot(properties?.asObservable() ?: MutableMeta()) + public val plot: Plot get() = Plot(meta) } public fun Plot.asVision(): VisionOfPlotly = VisionOfPlotly(this) diff --git a/visionforge-server/src/main/kotlin/space/kscience/visionforge/three/server/VisionServer.kt b/visionforge-server/src/main/kotlin/space/kscience/visionforge/three/server/VisionServer.kt index 0a9c60e3..46cb2e66 100644 --- a/visionforge-server/src/main/kotlin/space/kscience/visionforge/three/server/VisionServer.kt +++ b/visionforge-server/src/main/kotlin/space/kscience/visionforge/three/server/VisionServer.kt @@ -137,7 +137,7 @@ public class VisionServer internal constructor( val change = visionManager.jsonFormat.decodeFromString( VisionChange.serializer(), it.data.decodeToString() ) - vision.change(change) + vision.update(change) } } diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/ColorAccessor.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/ColorAccessor.kt index 66efe69b..25631c42 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/ColorAccessor.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/ColorAccessor.kt @@ -6,20 +6,23 @@ import space.kscience.dataforge.values.Value import space.kscience.dataforge.values.asValue import space.kscience.dataforge.values.string import space.kscience.visionforge.Colors +import space.kscience.visionforge.Vision import space.kscience.visionforge.VisionBuilder +import space.kscience.visionforge.VisionPropertyContainer +import kotlin.jvm.JvmInline @VisionBuilder -public class ColorAccessor(private val parent: MutableMetaProvider, private val colorKey: Name) { +public class ColorAccessor(private val colorKey: Name, private val parent: () -> MutableMetaProvider) { public var value: Value? - get() = parent.getMeta(colorKey)?.value + get() = parent().getMeta(colorKey)?.value set(value) { - parent.setValue(colorKey,value) + parent().setValue(colorKey,value) } public var item: Meta? - get() = parent.getMeta(colorKey) + get() = parent().getMeta(colorKey) set(value) { - parent.setMeta(colorKey,value) + parent().setMeta(colorKey,value) } } diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Composite.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Composite.kt index 4017b11b..b3770d37 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Composite.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Composite.kt @@ -2,6 +2,7 @@ package space.kscience.visionforge.solid import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +import space.kscience.dataforge.meta.configure import space.kscience.dataforge.meta.update import space.kscience.visionforge.* @@ -29,20 +30,20 @@ public inline fun VisionContainerBuilder.composite( val group = SolidGroup().apply(builder) val children = group.children.values.filterIsInstance() if (children.size != 2) error("Composite requires exactly two children") - return Composite(type, children[0], children[1]).also { composite -> - composite.configure { - update(group.meta()) + return Composite(type, children[0], children[1]).apply { + configure { + update(group.meta) } if (group.position != null) { - composite.position = group.position + position = group.position } if (group.rotation != null) { - composite.rotation = group.rotation + rotation = group.rotation } if (group.scale != null) { - composite.scale = group.scale + scale = group.scale } - set(name, composite) + set(name, this) } } diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Extruded.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Extruded.kt index 0ecc02b0..b9e32bee 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Extruded.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Extruded.kt @@ -4,6 +4,7 @@ import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import space.kscience.dataforge.meta.MutableMeta import space.kscience.dataforge.meta.ObservableMutableMeta +import space.kscience.dataforge.meta.configure import space.kscience.visionforge.* import kotlin.math.PI import kotlin.math.cos @@ -106,7 +107,9 @@ public class ExtrudeBuilder( layers.add(Layer(x.toFloat(), y.toFloat(), z.toFloat(), scale.toFloat())) } - internal fun build(): Extruded = Extruded(shape, layers).apply { configure(meta()) } + internal fun build(): Extruded = Extruded(shape, layers).apply { + configure(this@ExtrudeBuilder.meta) + } } @VisionBuilder diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Solid.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Solid.kt index f2ad38fd..39ed06ac 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Solid.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Solid.kt @@ -5,8 +5,7 @@ import space.kscience.dataforge.meta.descriptors.* import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.plus -import space.kscience.dataforge.values.ValueType -import space.kscience.dataforge.values.asValue +import space.kscience.dataforge.values.* import space.kscience.visionforge.* import space.kscience.visionforge.Vision.Companion.VISIBLE_KEY import space.kscience.visionforge.solid.Solid.Companion.DETAIL_KEY @@ -76,6 +75,8 @@ public interface Solid : Vision { default(true) } + node(SolidMaterial.MATERIAL_KEY.toString(), SolidMaterial) + //TODO replace by descriptor merge value(Vision.STYLE_KEY, ValueType.STRING) { multiple = true @@ -98,10 +99,6 @@ public interface Solid : Vision { hide() } - item(SolidMaterial.MATERIAL_KEY.toString(), SolidMaterial){ - valueRequirement = ValueRequirement.ABSENT - } - enum(ROTATION_ORDER_KEY, default = RotationOrder.XYZ) { hide() } @@ -114,7 +111,7 @@ public interface Solid : Vision { * Get the layer number this solid belongs to. Return 0 if layer is not defined. */ public var Solid.layer: Int - get() = getProperty(LAYER_KEY, inherit = true).int ?: 0 + get() = getPropertyValue(LAYER_KEY, inherit = true)?.int ?: 0 set(value) { setProperty(LAYER_KEY, value) } @@ -134,24 +131,24 @@ public enum class RotationOrder { * Rotation order */ public var Solid.rotationOrder: RotationOrder - get() = getProperty(Solid.ROTATION_ORDER_KEY).enum() ?: RotationOrder.XYZ - set(value) = setPropertyValue(Solid.ROTATION_ORDER_KEY, value.name.asValue()) + get() = getPropertyValue(Solid.ROTATION_ORDER_KEY)?.enum() ?: RotationOrder.XYZ + set(value) = meta.setValue(Solid.ROTATION_ORDER_KEY, value.name.asValue()) /** * Preferred number of polygons for displaying the object. If not defined, uses shape or renderer default. Not inherited */ public var Solid.detail: Int? - get() = getProperty(DETAIL_KEY, false).int - set(value) = setPropertyValue(DETAIL_KEY, value?.asValue()) + get() = getPropertyValue(DETAIL_KEY, false)?.int + set(value) = meta.setValue(DETAIL_KEY, value?.asValue()) /** * If this property is true, the object will be ignored on render. * Property is not inherited. */ public var Vision.ignore: Boolean? - get() = getProperty(IGNORE_KEY, false).boolean - set(value) = setPropertyValue(IGNORE_KEY, value?.asValue()) + get() = getPropertyValue(IGNORE_KEY, false)?.boolean + set(value) = meta.setValue(IGNORE_KEY, value?.asValue()) //var VisualObject.selected: Boolean? // get() = getProperty(SELECTED_KEY).boolean @@ -160,7 +157,7 @@ public var Vision.ignore: Boolean? internal fun float(name: Name, default: Number): ReadWriteProperty = object : ReadWriteProperty { override fun getValue(thisRef: Solid, property: KProperty<*>): Number { - return thisRef.getOwnProperty(name)?.number ?: default + return thisRef.meta.getMeta(name)?.number ?: default } override fun setValue(thisRef: Solid, property: KProperty<*>, value: Number) { @@ -171,7 +168,7 @@ internal fun float(name: Name, default: Number): ReadWriteProperty = object : ReadWriteProperty { override fun getValue(thisRef: Solid, property: KProperty<*>): Point3D? { - val item = thisRef.getOwnProperty(name) ?: return null + val item = thisRef.meta.getMeta(name) ?: return null return object : Point3D { override val x: Float get() = item[X_KEY]?.float ?: default override val y: Float get() = item[Y_KEY]?.float ?: default @@ -181,7 +178,7 @@ internal fun point(name: Name, default: Float): ReadWriteProperty, value: Point3D?) { if (value == null) { - thisRef.setPropertyNode(name, null) + thisRef.meta.setMeta(name, null) } else { thisRef.setProperty(name + X_KEY, value.x) thisRef.setProperty(name + Y_KEY, value.y) diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidBase.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidBase.kt index 2beee1ed..f18a3869 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidBase.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidBase.kt @@ -11,8 +11,8 @@ import space.kscience.visionforge.VisionChange public open class SolidBase : VisionBase(), Solid { override val descriptor: MetaDescriptor get() = Solid.descriptor - override fun change(change: VisionChange) { + override fun update(change: VisionChange) { updatePosition(change.properties) - super.change(change) + super.update(change) } } diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidGroup.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidGroup.kt index bc6676f6..f0f28fc5 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidGroup.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidGroup.kt @@ -60,9 +60,9 @@ public class SolidGroup : VisionGroupBase(), Solid, PrototypeHolder { override fun createGroup(): SolidGroup = SolidGroup() - override fun change(change: VisionChange) { + override fun update(change: VisionChange) { updatePosition(change.properties) - super.change(change) + super.update(change) } public companion object { diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidMaterial.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidMaterial.kt index 3d9843a3..78d787a3 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidMaterial.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidMaterial.kt @@ -8,6 +8,7 @@ import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.plus import space.kscience.dataforge.values.ValueType import space.kscience.dataforge.values.asValue +import space.kscience.dataforge.values.number import space.kscience.visionforge.* import space.kscience.visionforge.solid.SolidMaterial.Companion.MATERIAL_COLOR_KEY import space.kscience.visionforge.solid.SolidMaterial.Companion.MATERIAL_KEY @@ -19,14 +20,14 @@ public class SolidMaterial : Scheme() { /** * Primary web-color for the material */ - public val color: ColorAccessor = ColorAccessor(this, COLOR_KEY) + public val color: ColorAccessor = ColorAccessor(COLOR_KEY) { meta } /** * Specular color for phong material */ - public val specularColor: ColorAccessor = ColorAccessor(this, SPECULAR_COLOR_KEY) + public val specularColor: ColorAccessor = ColorAccessor(SPECULAR_COLOR_KEY) { meta } - public val emissiveColor: ColorAccessor = ColorAccessor(this, "emissiveColor".asName()) + public val emissiveColor: ColorAccessor = ColorAccessor("emissiveColor".asName()) { meta } /** * Opacity @@ -89,24 +90,19 @@ public class SolidMaterial : Scheme() { } public val Solid.color: ColorAccessor - get() = ColorAccessor( - meta(inherit = true), - MATERIAL_COLOR_KEY - ) + get() = ColorAccessor(MATERIAL_COLOR_KEY) { computeProperties() } public var Solid.material: SolidMaterial? - get() = getProperty(MATERIAL_KEY, inherit = true)?.let { SolidMaterial.read(it) } - set(value) = setPropertyNode(MATERIAL_KEY, value?.meta) + get() = computePropertyNode(MATERIAL_KEY)?.let { SolidMaterial.read(it) } + set(value) = meta.setMeta(MATERIAL_KEY, value?.meta) @VisionBuilder public fun Solid.material(builder: SolidMaterial.() -> Unit) { - configure(MATERIAL_KEY){ - updateWith(SolidMaterial,builder) - } + meta.getOrCreate(MATERIAL_KEY).updateWith(SolidMaterial, builder) } public var Solid.opacity: Number? - get() = getProperty(MATERIAL_OPACITY_KEY, inherit = true).number + get() = getPropertyValue(MATERIAL_OPACITY_KEY, inherit = true)?.number set(value) { - setPropertyValue(MATERIAL_OPACITY_KEY, value?.asValue()) + meta.setValue(MATERIAL_OPACITY_KEY, value?.asValue()) } \ No newline at end of file diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidReference.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidReference.kt index 0149fc88..427ef9a2 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidReference.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidReference.kt @@ -1,12 +1,10 @@ package space.kscience.visionforge.solid -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.mapNotNull import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.ObservableMutableMeta import space.kscience.dataforge.meta.descriptors.MetaDescriptor -import space.kscience.dataforge.misc.DFExperimental +import space.kscience.dataforge.meta.get import space.kscience.dataforge.names.* import space.kscience.dataforge.values.Value import space.kscience.visionforge.* @@ -17,6 +15,18 @@ public interface SolidReference : VisionGroup { * The prototype for this reference. Always returns a "real" prototype, not a reference */ public val prototype: Solid + + override fun getPropertyValue(name: Name, inherit: Boolean, includeStyles: Boolean, includeDefaults: Boolean): Value? { + meta[name]?.value?.let { return it } + if (includeStyles) { + getStyleProperty(name)?.let { return it } + } + prototype.getPropertyValue(name, inherit, includeStyles, includeDefaults)?.let { return it } + if (inherit) { + parent?.getPropertyValue(name, inherit, includeStyles, includeDefaults)?.let { return it } + } + return null + } } @@ -31,27 +41,6 @@ public val Vision.unref: Solid else -> error("This Vision is neither Solid nor SolidReference") } - -private fun SolidReference.getRefProperty( - name: Name, - inherit: Boolean, - includeStyles: Boolean, - includeDefaults: Boolean, -): Meta? = if (!inherit && !includeStyles && !includeDefaults) { - getOwnProperty(name) -} else { - buildList { - add(getOwnProperty(name)) - if (includeStyles) { - addAll(getStyleItems(name)) - } - add(prototype.getProperty(name, inherit, includeStyles, includeDefaults)) - if (inherit) { - add(parent?.getProperty(name, inherit)) - } - }.merge() -} - private fun childToken(childName: Name): NameToken = NameToken(SolidReferenceGroup.REFERENCE_CHILD_PROPERTY_PREFIX, childName.toString()) @@ -83,12 +72,8 @@ public class SolidReferenceGroup( ReferenceChild(this, it.key.asName()) } ?: emptyMap() - override fun getProperty( - name: Name, - inherit: Boolean, - includeStyles: Boolean, - includeDefaults: Boolean, - ): Meta? = getRefProperty(name, inherit, includeStyles, includeDefaults) + override fun getPropertyValue(name: Name, inherit: Boolean, includeStyles: Boolean, includeDefaults: Boolean): Value? = + super.getPropertyValue(name, inherit, includeStyles, includeDefaults) override val descriptor: MetaDescriptor get() = prototype.descriptor @@ -111,6 +96,10 @@ public class SolidReferenceGroup( } } + override val meta: ObservableMutableMeta by lazy { + owner.meta.getOrCreate(childToken(refName).asName()) + } + override val children: Map get() = (prototype as? VisionGroup)?.children ?.filter { it.key != SolidGroup.PROTOTYPES_TOKEN } @@ -118,24 +107,6 @@ public class SolidReferenceGroup( ReferenceChild(owner, refName + key.asName()) } ?: emptyMap() - override fun getOwnProperty(name: Name): Meta? = - owner.getOwnProperty(childPropertyName(refName, name)) - - override fun setPropertyNode(name: Name, node: Meta?, notify: Boolean) { - 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( - name: Name, - inherit: Boolean, - includeStyles: Boolean, - includeDefaults: Boolean, - ): Meta? = getRefProperty(name, inherit, includeStyles, includeDefaults) - override var parent: VisionGroup? get() { val parentName = refName.cutLast() @@ -145,21 +116,11 @@ public class SolidReferenceGroup( error("Setting a parent for a reference child is not possible") } - @DFExperimental - override val propertyChanges: Flow - get() = owner.propertyChanges.mapNotNull { name -> - if (name.startsWith(childToken(refName))) { - name.cutFirst() - } else { - null - } - } - override fun invalidateProperty(propertyName: Name) { owner.invalidateProperty(childPropertyName(refName, propertyName)) } - override fun change(change: VisionChange) { + override fun update(change: VisionChange) { change.properties?.let { updateProperties(Name.EMPTY, it) } diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Axes.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Axes.kt index 8f980dad..485cc8bd 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Axes.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Axes.kt @@ -4,8 +4,8 @@ import space.kscience.dataforge.meta.Scheme import space.kscience.dataforge.meta.SchemeSpec import space.kscience.dataforge.meta.boolean import space.kscience.dataforge.meta.descriptors.MetaDescriptor +import space.kscience.dataforge.meta.descriptors.value import space.kscience.dataforge.meta.double -import space.kscience.visionforge.value public class Axes : Scheme() { public var visible: Boolean by boolean(false) diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Camera.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Camera.kt index 2319fa69..b8cb05d4 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Camera.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Camera.kt @@ -3,9 +3,9 @@ package space.kscience.visionforge.solid.specifications import space.kscience.dataforge.meta.Scheme import space.kscience.dataforge.meta.SchemeSpec import space.kscience.dataforge.meta.descriptors.MetaDescriptor +import space.kscience.dataforge.meta.descriptors.value import space.kscience.dataforge.meta.double import space.kscience.dataforge.meta.int -import space.kscience.visionforge.value import kotlin.math.PI public class Camera : Scheme() { @@ -27,24 +27,24 @@ public class Camera : Scheme() { public const val FAR_CLIP: Double = 10000.0 public const val FIELD_OF_VIEW: Int = 75 - override val descriptor: MetaDescriptor by lazy { + override val descriptor: MetaDescriptor by lazy { MetaDescriptor { - value(Camera::fov){ + value(Camera::fov) { default(FIELD_OF_VIEW) } - value(Camera::nearClip){ + value(Camera::nearClip) { default(NEAR_CLIP) } - value(Camera::farClip){ + value(Camera::farClip) { default(FAR_CLIP) } - value(Camera::distance){ + value(Camera::distance) { default(INITIAL_DISTANCE) } - value(Camera::azimuth){ + value(Camera::azimuth) { default(INITIAL_AZIMUTH) } - value(Camera::latitude){ + value(Camera::latitude) { default(INITIAL_LATITUDE) } } diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Canvas3DOptions.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Canvas3DOptions.kt index ea546821..01f6e74e 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Canvas3DOptions.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Canvas3DOptions.kt @@ -2,11 +2,11 @@ package space.kscience.visionforge.solid.specifications import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.descriptors.MetaDescriptor +import space.kscience.dataforge.meta.descriptors.scheme +import space.kscience.dataforge.meta.descriptors.value import space.kscience.dataforge.names.Name import space.kscience.dataforge.values.ValueType import space.kscience.visionforge.hide -import space.kscience.visionforge.scheme -import space.kscience.visionforge.value import space.kscience.visionforge.widgetType public class Clipping : Scheme() { @@ -80,7 +80,19 @@ public class Canvas3DOptions : Scheme() { override val descriptor: MetaDescriptor by lazy { MetaDescriptor { scheme(Canvas3DOptions::axes, Axes) - scheme(Canvas3DOptions::light, Light) + + value(Canvas3DOptions::layers) { + multiple = true + default(listOf(0)) + widgetType = "multiSelect" + allowedValues(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + } + + scheme(Canvas3DOptions::clipping, Clipping) + + scheme(Canvas3DOptions::light, Light){ + hide() + } scheme(Canvas3DOptions::camera, Camera) { hide() @@ -93,15 +105,6 @@ public class Canvas3DOptions : Scheme() { scheme(Canvas3DOptions::size, CanvasSize) { hide() } - - value(Canvas3DOptions::layers) { - type(ValueType.NUMBER) - multiple = true - default(listOf(0)) - widgetType = "multiSelect" - allowedValues(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10) - } - scheme(Canvas3DOptions::clipping, Clipping) } } } diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/transform/RemoveSingleChild.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/transform/RemoveSingleChild.kt index e623ef46..39aadbf1 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/transform/RemoveSingleChild.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/transform/RemoveSingleChild.kt @@ -1,5 +1,6 @@ package space.kscience.visionforge.solid.transform +import space.kscience.dataforge.meta.configure import space.kscience.dataforge.meta.update import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.names.asName @@ -22,7 +23,7 @@ internal fun Vision.updateFrom(other: Vision): Vision { scaleY *= other.scaleY scaleZ *= other.scaleZ configure{ - update(other.meta()) + update(other.meta) } } return this diff --git a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/PropertyTest.kt b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/PropertyTest.kt index a31ed9f8..d146195a 100644 --- a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/PropertyTest.kt +++ b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/PropertyTest.kt @@ -1,13 +1,24 @@ package space.kscience.visionforge.solid -import space.kscience.dataforge.meta.int +import space.kscience.dataforge.meta.* import space.kscience.dataforge.names.asName +import space.kscience.dataforge.values.int import space.kscience.visionforge.* import kotlin.test.Test import kotlin.test.assertEquals @Suppress("UNUSED_VARIABLE") class PropertyTest { + @Test + fun testColorUpdate(){ + val box = Box(10.0f, 10.0f,10.0f) + box.material { + //meta["color"] = "pink" + color("pink") + } + assertEquals("pink", box.meta["material.color"]?.string) + } + @Test fun testInheritedProperty() { var box: Box? = null @@ -17,7 +28,7 @@ class PropertyTest { box = box(100, 100, 100) } } - assertEquals(22, box?.getProperty("test", inherit = true).int) + assertEquals(22, box?.getPropertyValue("test", inherit = true)?.int) } @Test @@ -35,11 +46,11 @@ class PropertyTest { } } } - assertEquals(22, box?.getProperty("test").int) + assertEquals(22, box?.getPropertyValue("test")?.int) } @Test - fun testColor() { + fun testStyleColor() { var box: Box? = null val group = SolidGroup().apply { styleSheet { diff --git a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/SerializationTest.kt b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/SerializationTest.kt index d878881f..8f5c3f57 100644 --- a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/SerializationTest.kt +++ b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/SerializationTest.kt @@ -3,7 +3,6 @@ package space.kscience.visionforge.solid import space.kscience.dataforge.names.Name import space.kscience.visionforge.MutableVisionGroup import space.kscience.visionforge.get -import space.kscience.visionforge.meta import kotlin.test.Test import kotlin.test.assertEquals @@ -32,7 +31,7 @@ class SerializationTest { val string = Solids.encodeToString(cube) println(string) val newCube = Solids.decodeFromString(string) - assertEquals(cube.meta(), newCube.meta()) + assertEquals(cube.meta, newCube.meta) } @Test @@ -53,7 +52,7 @@ class SerializationTest { val string = Solids.encodeToString(group) println(string) val reconstructed = Solids.decodeFromString(string) as SolidGroup - assertEquals(group["cube"]?.meta(), reconstructed["cube"]?.meta()) + assertEquals(group["cube"]?.meta, reconstructed["cube"]?.meta) } } \ No newline at end of file diff --git a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/VisionUpdateTest.kt b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/VisionUpdateTest.kt index 821e7f04..7a36ab3f 100644 --- a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/VisionUpdateTest.kt +++ b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/VisionUpdateTest.kt @@ -28,7 +28,7 @@ class VisionUpdateTest { propertyChanged("top".asName(), SolidMaterial.MATERIAL_COLOR_KEY, Meta("red".asValue())) propertyChanged("origin".asName(), SolidMaterial.MATERIAL_COLOR_KEY, Meta("red".asValue())) } - targetVision.change(dif) + targetVision.update(dif) assertTrue { targetVision["top"] is SolidGroup } assertEquals("red", (targetVision["origin"] as Solid).color.string) // Should work assertEquals("#00007b", (targetVision["top"] as Solid).color.string) // new item always takes precedence diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/MeshThreeFactory.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/MeshThreeFactory.kt index 7e90a657..992035a6 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/MeshThreeFactory.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/MeshThreeFactory.kt @@ -4,12 +4,13 @@ import info.laht.threekt.core.BufferGeometry import info.laht.threekt.geometries.EdgesGeometry import info.laht.threekt.objects.LineSegments import info.laht.threekt.objects.Mesh -import space.kscience.dataforge.meta.boolean -import space.kscience.dataforge.meta.node +import space.kscience.dataforge.meta.get import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.plus import space.kscience.dataforge.names.startsWith +import space.kscience.dataforge.values.boolean +import space.kscience.visionforge.computeProperties import space.kscience.visionforge.onPropertyChange import space.kscience.visionforge.solid.Solid import space.kscience.visionforge.solid.SolidMaterial @@ -40,7 +41,7 @@ public abstract class MeshThreeFactory( } //add listener to object properties - obj.onPropertyChange(three.updateScope) { name -> + obj.onPropertyChange { name -> when { name.startsWith(Solid.GEOMETRY_KEY) -> { val oldGeometry = mesh.geometry as BufferGeometry @@ -83,14 +84,10 @@ internal fun Mesh.applyProperties(obj: Solid): Mesh = apply { public fun Mesh.applyEdges(obj: Solid) { val edges = children.find { it.name == "@edges" } as? LineSegments //inherited edges definition, enabled by default - if (obj.getProperty(MeshThreeFactory.EDGES_ENABLED_KEY, inherit = true, includeStyles = true).boolean != false) { + if (obj.getPropertyValue(MeshThreeFactory.EDGES_ENABLED_KEY, inherit = true, includeStyles = true)?.boolean != false) { val bufferGeometry = geometry as? BufferGeometry ?: return val material = ThreeMaterials.getLineMaterial( - obj.getProperty( - MeshThreeFactory.EDGES_MATERIAL_KEY, - inherit = true, - includeStyles = true - ), + obj.computeProperties().get(MeshThreeFactory.EDGES_MATERIAL_KEY), true ) if (edges == null) { diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCompositeFactory.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCompositeFactory.kt index 2cdd12d3..5705459a 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCompositeFactory.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCompositeFactory.kt @@ -48,7 +48,7 @@ public class ThreeCompositeFactory(public val three: ThreePlugin) : ThreeFactory }.apply { updatePosition(obj) applyProperties(obj) - obj.onPropertyChange(three.updateScope) { name -> + obj.onPropertyChange { name -> when { //name.startsWith(WIREFRAME_KEY) -> mesh.applyWireFrame(obj) name.startsWith(MeshThreeFactory.EDGES_KEY) -> applyEdges(obj) diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeGeometryBuilder.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeGeometryBuilder.kt index c1c5aa72..fc226de6 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeGeometryBuilder.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeGeometryBuilder.kt @@ -40,7 +40,7 @@ public class ThreeGeometryBuilder : GeometryBuilder { } override fun face(vertex1: Point3D, vertex2: Point3D, vertex3: Point3D, normal: Point3D?, meta: Meta) { - val actualNormal: Point3D = normal ?: (vertex3 - vertex2) cross (vertex1 - vertex2) + val actualNormal: Point3D = normal ?: ((vertex3 - vertex2) cross (vertex1 - vertex2)) indices.add( vertex(vertex1, actualNormal), vertex(vertex2, actualNormal), diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeLabelFactory.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeLabelFactory.kt index d0c10605..34ee5ed1 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeLabelFactory.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeLabelFactory.kt @@ -27,7 +27,7 @@ public object ThreeLabelFactory : ThreeFactory { return Mesh(textGeo, ThreeMaterials.DEFAULT).apply { updateMaterial(obj) updatePosition(obj) - obj.onPropertyChange(three.updateScope) { _ -> + obj.onPropertyChange { _ -> //TODO three.logger.warn { "Label parameter change not implemented" } } diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeLineFactory.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeLineFactory.kt index bc369086..49fca0d9 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeLineFactory.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeLineFactory.kt @@ -4,6 +4,7 @@ import info.laht.threekt.core.BufferGeometry import info.laht.threekt.core.Object3D import info.laht.threekt.math.Color import info.laht.threekt.objects.LineSegments +import space.kscience.visionforge.computePropertyNode import space.kscience.visionforge.onPropertyChange import space.kscience.visionforge.solid.PolyLine import space.kscience.visionforge.solid.color @@ -20,7 +21,7 @@ public object ThreeLineFactory : ThreeFactory { } val material = ThreeMaterials.getLineMaterial( - obj.getProperty(MeshThreeFactory.EDGES_MATERIAL_KEY), + obj.computePropertyNode(MeshThreeFactory.EDGES_MATERIAL_KEY), true ) @@ -31,7 +32,7 @@ public object ThreeLineFactory : ThreeFactory { updatePosition(obj) //layers.enable(obj.layer) //add listener to object properties - obj.onPropertyChange(three.updateScope) { propertyName -> + obj.onPropertyChange { propertyName -> updateProperty(obj, propertyName) } } diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeMaterials.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeMaterials.kt index 258df5ff..9a3c6be9 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeMaterials.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeMaterials.kt @@ -8,12 +8,10 @@ import info.laht.threekt.math.Color import info.laht.threekt.objects.Mesh import space.kscience.dataforge.meta.* import space.kscience.dataforge.names.Name -import space.kscience.dataforge.values.ValueType -import space.kscience.dataforge.values.int -import space.kscience.dataforge.values.string +import space.kscience.dataforge.values.* import space.kscience.visionforge.Colors import space.kscience.visionforge.Vision -import space.kscience.visionforge.meta +import space.kscience.visionforge.computePropertyNode import space.kscience.visionforge.solid.SolidMaterial @@ -117,8 +115,8 @@ private var Material.cached: Boolean public fun Mesh.updateMaterial(vision: Vision) { //val meta = vision.getProperty(SolidMaterial.MATERIAL_KEY, inherit = true).node - val ownMaterialMeta = vision.meta()[SolidMaterial.MATERIAL_KEY] - val parentMaterialMeta = vision.parent?.getProperty( + val ownMaterialMeta = vision.meta.getMeta(SolidMaterial.MATERIAL_KEY) + val parentMaterialMeta = vision.parent?.getPropertyValue( SolidMaterial.MATERIAL_KEY, inherit = true, includeStyles = false, @@ -128,19 +126,15 @@ public fun Mesh.updateMaterial(vision: Vision) { material = when { ownMaterialMeta == null && parentMaterialMeta == null -> { //If material is style-based, use cached - vision.getProperty( + vision.computePropertyNode( SolidMaterial.MATERIAL_KEY, - inherit = false, - includeStyles = true, - includeDefaults = false )?.let { ThreeMaterials.cacheMaterial(it) } ?: ThreeMaterials.DEFAULT } else -> { - vision.getProperty( + vision.computePropertyNode( SolidMaterial.MATERIAL_KEY, - inherit = true )?.let { ThreeMaterials.buildMaterial(it) } ?: ThreeMaterials.DEFAULT @@ -155,32 +149,29 @@ public fun Mesh.updateMaterialProperty(vision: Vision, propertyName: Name) { } else { when (propertyName) { SolidMaterial.MATERIAL_COLOR_KEY -> { - material.asDynamic().color = vision.getProperty( + material.asDynamic().color = vision.computePropertyNode( SolidMaterial.MATERIAL_COLOR_KEY, - inherit = true, - includeStyles = true, - includeDefaults = false )?.threeColor() ?: ThreeMaterials.DEFAULT_COLOR material.needsUpdate = true } SolidMaterial.MATERIAL_OPACITY_KEY -> { - val opacity = vision.getProperty( + val opacity = vision.getPropertyValue( SolidMaterial.MATERIAL_OPACITY_KEY, inherit = true, includeStyles = true, includeDefaults = false - ).double ?: 1.0 + )?.double ?: 1.0 material.opacity = opacity material.transparent = opacity < 1.0 material.needsUpdate = true } SolidMaterial.MATERIAL_WIREFRAME_KEY -> { - material.asDynamic().wireframe = vision.getProperty( + material.asDynamic().wireframe = vision.getPropertyValue( SolidMaterial.MATERIAL_WIREFRAME_KEY, inherit = true, includeStyles = true, includeDefaults = false - ).boolean ?: false + )?.boolean ?: false material.needsUpdate = true } else -> console.warn("Unrecognized material property: $propertyName") diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreePlugin.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreePlugin.kt index 9c7c1f6b..e7c2ac86 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreePlugin.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreePlugin.kt @@ -69,7 +69,7 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer { updatePosition(obj) //obj.onChildrenChange() - obj.onPropertyChange(updateScope) { name -> + obj.onPropertyChange { name -> if ( name.startsWith(Solid.POSITION_KEY) || name.startsWith(Solid.ROTATION_KEY) || diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeReferenceFactory.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeReferenceFactory.kt index 4dca0eeb..53e9c2c9 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeReferenceFactory.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeReferenceFactory.kt @@ -47,7 +47,7 @@ public object ThreeReferenceFactory : ThreeFactory { //TODO apply child properties - obj.onPropertyChange(three.updateScope) { name-> + obj.onPropertyChange { name-> if (name.firstOrNull()?.body == REFERENCE_CHILD_PROPERTY_PREFIX) { val childName = name.firstOrNull()?.index?.let(Name::parse) ?: error("Wrong syntax for reference child property: '$name'") val propertyName = name.cutFirst() diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/csg.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/csg.kt index 50e48ee9..ac2fefc0 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/csg.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/csg.kt @@ -3,7 +3,8 @@ "OVERRIDING_FINAL_MEMBER", "RETURN_TYPE_MISMATCH_ON_OVERRIDE", "CONFLICTING_OVERLOADS", - "EXTERNAL_DELEGATION" + "EXTERNAL_DELEGATION", + "NO_EXPLICIT_VISIBILITY_IN_API_MODE_WARNING" ) @file:JsModule("three-csg-ts")