0.2.0 #71

Merged
altavir merged 139 commits from dev into master 2022-01-24 09:44:18 +03:00
58 changed files with 524 additions and 784 deletions
Showing only changes of commit 7b78052f61 - Show all commits

View File

@ -2,7 +2,7 @@ plugins {
id("ru.mipt.npm.gradle.project") 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") val fxVersion by extra("11")
allprojects { allprojects {

View File

@ -16,7 +16,7 @@ kotlin {
jvm { jvm {
withJava() withJava()
} }
js{ js {
useCommonJs() useCommonJs()
browser { browser {
commonWebpackConfig { commonWebpackConfig {
@ -34,6 +34,7 @@ kotlin {
jvmMain { jvmMain {
dependencies { dependencies {
implementation(project(":visionforge-fx")) implementation(project(":visionforge-fx"))
implementation("ch.qos.logback:logback-classic:1.2.5")
} }
} }
jsMain { jsMain {
@ -53,5 +54,5 @@ application {
val convertGdmlToJson by tasks.creating(JavaExec::class) { val convertGdmlToJson by tasks.creating(JavaExec::class) {
group = "application" group = "application"
classpath = sourceSets["main"].runtimeClasspath classpath = sourceSets["main"].runtimeClasspath
main = "space.kscience.dataforge.vis.spatial.gdml.demo.SaveToJsonKt" mainClass.set("space.kscience.dataforge.vis.spatial.gdml.demo.SaveToJsonKt")
} }

View File

@ -1,8 +1,8 @@
package space.kscience.visionforge.gdml package space.kscience.visionforge.gdml
import space.kscience.dataforge.meta.string
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.values.asValue import space.kscience.dataforge.values.asValue
import space.kscience.dataforge.values.string
import space.kscience.gdml.GdmlShowCase import space.kscience.gdml.GdmlShowCase
import space.kscience.visionforge.setProperty import space.kscience.visionforge.setProperty
import space.kscience.visionforge.solid.SolidMaterial import space.kscience.visionforge.solid.SolidMaterial
@ -27,6 +27,6 @@ class GDMLVisionTest {
val child = vision[Name.of("composite-000","segment-0")] val child = vision[Name.of("composite-000","segment-0")]
assertNotNull(child) assertNotNull(child)
child.setProperty(SolidMaterial.MATERIAL_COLOR_KEY, "red".asValue()) child.setProperty(SolidMaterial.MATERIAL_COLOR_KEY, "red".asValue())
assertEquals("red", child.getProperty(SolidMaterial.MATERIAL_COLOR_KEY).string) assertEquals("red", child.getPropertyValue(SolidMaterial.MATERIAL_COLOR_KEY)?.string)
} }
} }

View File

@ -7,7 +7,7 @@ import space.kscience.dataforge.context.Context
import space.kscience.dataforge.context.fetch import space.kscience.dataforge.context.fetch
import space.kscience.gdml.GdmlShowCase import space.kscience.gdml.GdmlShowCase
import space.kscience.visionforge.VisionManager import space.kscience.visionforge.VisionManager
import space.kscience.visionforge.describedProperties import space.kscience.visionforge.computeProperties
import space.kscience.visionforge.editor.VisionEditorFragment import space.kscience.visionforge.editor.VisionEditorFragment
import space.kscience.visionforge.editor.VisionTreeFragment import space.kscience.visionforge.editor.VisionTreeFragment
import space.kscience.visionforge.gdml.toVision import space.kscience.visionforge.gdml.toVision
@ -34,10 +34,10 @@ class GDMLView : View() {
} }
private val propertyEditor = VisionEditorFragment { private val propertyEditor = VisionEditorFragment {
it.describedProperties it.computeProperties()
}.apply { }.apply {
descriptorProperty.set(SolidMaterial.descriptor) descriptorProperty.set(SolidMaterial.descriptor)
itemProperty.bind(treeFragment.selectedProperty) visionProperty.bind(treeFragment.selectedProperty)
} }
override val root: Parent = borderpane { override val root: Parent = borderpane {

View File

@ -1,9 +1,6 @@
package space.kscience.visionforge.solid.demo package space.kscience.visionforge.solid.demo
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.*
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.invoke import space.kscience.dataforge.meta.invoke
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
@ -76,7 +73,7 @@ fun VisionLayout<Solid>.showcase() {
//override color for this cube //override color for this cube
color(1530) color(1530)
launch(Dispatchers.Main) { GlobalScope.launch(Dispatchers.Main) {
while (isActive) { while (isActive) {
delay(500) delay(500)
visible = !(visible ?: false) visible = !(visible ?: false)
@ -85,7 +82,7 @@ fun VisionLayout<Solid>.showcase() {
} }
} }
launch(Dispatchers.Main) { GlobalScope.launch(Dispatchers.Main) {
val random = Random(111) val random = Random(111)
while (isActive) { while (isActive) {
delay(1000) delay(1000)

View File

@ -3,6 +3,7 @@ package space.kscience.visionforge.solid.demo
import info.laht.threekt.core.Object3D import info.laht.threekt.core.Object3D
import info.laht.threekt.geometries.BoxGeometry import info.laht.threekt.geometries.BoxGeometry
import info.laht.threekt.objects.Mesh import info.laht.threekt.objects.Mesh
import space.kscience.dataforge.meta.get
import space.kscience.dataforge.meta.int import space.kscience.dataforge.meta.int
import space.kscience.dataforge.meta.number import space.kscience.dataforge.meta.number
import space.kscience.dataforge.names.asName 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) 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 //add listener to object properties
onPropertyChange(three.context) { name -> onPropertyChange { name ->
when { when {
name == VALUE -> { name == VALUE -> {
val value = getOwnProperty(VALUE).int ?: 0 val value = meta.get(VALUE).int ?: 0
val size = value.toFloat() / 255f * 20f val size = value.toFloat() / 255f * 20f
mesh.scale.z = size.toDouble() mesh.scale.z = size.toDouble()
mesh.position.z = size.toDouble() / 2 mesh.position.z = size.toDouble() / 2
@ -69,7 +70,7 @@ internal class VariableBox(val xSize: Number, val ySize: Number) : ThreeJsVision
} }
var value: Int var value: Int
get() = getOwnProperty(VALUE).int ?: 0 get() = meta[VALUE].int ?: 0
set(value) { set(value) {
setProperty(VALUE, value.asValue()) setProperty(VALUE, value.asValue())
} }

View File

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

View File

@ -4,13 +4,10 @@ import org.w3c.dom.Element
import react.RBuilder import react.RBuilder
import react.dom.render import react.dom.render
import space.kscience.dataforge.meta.descriptors.MetaDescriptor import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.visionforge.Vision import space.kscience.visionforge.*
import space.kscience.visionforge.getStyle
import space.kscience.visionforge.meta
import space.kscience.visionforge.react.metaViewer import space.kscience.visionforge.react.metaViewer
import space.kscience.visionforge.react.propertyEditor import space.kscience.visionforge.react.propertyEditor
import space.kscience.visionforge.solid.SolidReference import space.kscience.visionforge.solid.SolidReference
import space.kscience.visionforge.styles
public fun RBuilder.visionPropertyEditor( public fun RBuilder.visionPropertyEditor(
vision: Vision, vision: Vision,
@ -20,8 +17,8 @@ public fun RBuilder.visionPropertyEditor(
card("Properties") { card("Properties") {
propertyEditor( propertyEditor(
ownProperties = vision.meta(false,false,false), ownProperties = vision.meta,
allProperties = vision.meta(), allProperties = vision.computeProperties(),
updateFlow = vision.propertyChanges, updateFlow = vision.propertyChanges,
descriptor = descriptor, descriptor = descriptor,
key = key key = key

View File

@ -14,7 +14,8 @@ import space.kscience.dataforge.names.NameToken
import space.kscience.dataforge.names.isEmpty import space.kscience.dataforge.names.isEmpty
import space.kscience.dataforge.names.length import space.kscience.dataforge.names.length
import space.kscience.visionforge.VisionGroup import space.kscience.visionforge.VisionGroup
import space.kscience.visionforge.meta import space.kscience.visionforge.computeProperties
import space.kscience.visionforge.propertyChanges
import space.kscience.visionforge.react.ThreeCanvasComponent import space.kscience.visionforge.react.ThreeCanvasComponent
import space.kscience.visionforge.react.flexColumn import space.kscience.visionforge.react.flexColumn
import space.kscience.visionforge.react.flexRow import space.kscience.visionforge.react.flexRow
@ -134,8 +135,8 @@ public val ThreeCanvasWithControls: FunctionComponent<ThreeCanvasWithControlsPro
} }
IslandContent { IslandContent {
propertyEditor( propertyEditor(
ownProperties = vision.meta(false, false, false), ownProperties = vision.meta,
allProperties = vision.meta(), allProperties = vision.computeProperties(),
updateFlow = vision.propertyChanges, updateFlow = vision.propertyChanges,
descriptor = vision.descriptor, descriptor = vision.descriptor,
key = selected key = selected

View File

@ -8,14 +8,11 @@ import ringui.Island
import ringui.SmartTabs import ringui.SmartTabs
import ringui.Tab import ringui.Tab
import space.kscience.dataforge.meta.descriptors.MetaDescriptor import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.visionforge.Vision import space.kscience.visionforge.*
import space.kscience.visionforge.getStyle
import space.kscience.visionforge.meta
import space.kscience.visionforge.react.flexColumn import space.kscience.visionforge.react.flexColumn
import space.kscience.visionforge.react.metaViewer import space.kscience.visionforge.react.metaViewer
import space.kscience.visionforge.react.propertyEditor import space.kscience.visionforge.react.propertyEditor
import space.kscience.visionforge.solid.SolidReference import space.kscience.visionforge.solid.SolidReference
import space.kscience.visionforge.styles
public fun RBuilder.ringPropertyEditor( public fun RBuilder.ringPropertyEditor(
vision: Vision, vision: Vision,
@ -31,8 +28,8 @@ public fun RBuilder.ringPropertyEditor(
flexColumn { flexColumn {
Island("Properties") { Island("Properties") {
propertyEditor( propertyEditor(
ownProperties = vision.meta(false,false,false), ownProperties = vision.meta,
allProperties = vision.meta(), allProperties = vision.computeProperties(),
updateFlow = vision.propertyChanges, updateFlow = vision.propertyChanges,
descriptor = descriptor, descriptor = descriptor,
key = key key = key

View File

@ -0,0 +1,53 @@
package space.kscience.visionforge
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.ObservableMutableMeta
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.Name
import space.kscience.dataforge.names.NameToken
import space.kscience.dataforge.names.plus
import space.kscience.dataforge.values.Value
private class ComputedVisionProperties(
public val vision: Vision,
public val rootName: Name,
public val visionDescriptor: MetaDescriptor
) : ObservableMutableMeta by vision.meta {
public val descriptor: MetaDescriptor? = visionDescriptor[rootName]
override val items: Map<NameToken, ObservableMutableMeta>
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]

View File

@ -5,6 +5,7 @@ import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.NameToken import space.kscience.dataforge.names.NameToken
import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.asName
import space.kscience.dataforge.names.plus import space.kscience.dataforge.names.plus
import space.kscience.dataforge.values.Value
import space.kscience.dataforge.values.asValue import space.kscience.dataforge.values.asValue
import kotlin.jvm.JvmInline import kotlin.jvm.JvmInline
@ -14,7 +15,7 @@ import kotlin.jvm.JvmInline
@JvmInline @JvmInline
public value class StyleSheet(private val owner: VisionGroup) { public value class StyleSheet(private val owner: VisionGroup) {
private val styleNode: Meta? get() = owner.getOwnProperty(STYLESHEET_KEY) private val styleNode: Meta? get() = owner.meta[STYLESHEET_KEY]
public val items: Map<NameToken, Meta>? get() = styleNode?.items public val items: Map<NameToken, Meta>? get() = styleNode?.items
@ -24,7 +25,7 @@ public value class StyleSheet(private val owner: VisionGroup) {
* Define a style without notifying owner * Define a style without notifying owner
*/ */
public fun define(key: String, style: Meta?) { public fun define(key: String, style: Meta?) {
owner.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. * List of names of styles applied to this object. Order matters. Not inherited.
*/ */
public var Vision.styles: List<String> public var Vision.styles: List<String>
get() = getOwnProperty(Vision.STYLE_KEY)?.stringList ?: emptyList() get() = meta.getMeta(Vision.STYLE_KEY)?.stringList ?: emptyList()
set(value) { 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. * Add style name to the list of styles to be resolved later. The style with given name does not necessary exist at the moment.
*/ */
public fun Vision.useStyle(name: String) { public fun Vision.useStyle(name: String) {
styles = (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]. * Find a style with given name for given [Vision]. The style is not necessary applied to this [Vision].
*/ */
public tailrec fun Vision.getStyle(name: String): Meta? = public tailrec fun Vision.getStyle(name: String): Meta? =
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 * Resolve an item in all style layers

View File

@ -1,27 +1,27 @@
package space.kscience.visionforge 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.Flow
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.*
import space.kscience.dataforge.meta.MutableMeta
import space.kscience.dataforge.meta.descriptors.Described import space.kscience.dataforge.meta.descriptors.Described
import space.kscience.dataforge.meta.descriptors.MetaDescriptor 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.misc.Type
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.asName
import space.kscience.dataforge.values.Value 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 space.kscience.visionforge.Vision.Companion.TYPE
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
/** /**
* A root type for display hierarchy * A root type for display hierarchy
*/ */
@Type(TYPE) @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. * 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 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 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, name: Name,
inherit: Boolean = false, inherit: Boolean = false,
includeStyles: Boolean = true, includeStyles: Boolean = true,
includeDefaults: 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<Name>
/** /**
* Notify all listeners that a property has been changed and should be invalidated * 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]. * Update this vision using a dif represented by [VisionChange].
*/ */
public fun change(change: VisionChange) public fun update(change: VisionChange)
override val descriptor: MetaDescriptor? 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) { @OptIn(ExperimentalCoroutinesApi::class)
propertyChanges.onEach(callback).launchIn(scope) @DFExperimental
} public val Vision.propertyChanges: Flow<Name>
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( public fun Vision.onPropertyChange(callback: Meta.(Name) -> Unit) {
inherit: Boolean = false, meta.onChange(null, callback)
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.configure(meta: Meta) {
configure(Name.EMPTY) {
update(meta)
}
}
public fun Vision.configure(block: MutableMeta.() -> Unit): Unit = configure(Meta(block))
public fun Vision.getOwnProperty(key: String): Meta? = getOwnProperty(Name.parse(key))
/** /**
* Get [Vision] property using key as a String * Get [Vision] property using key as a String
*/ */
public fun Vision.getProperty( public fun Vision.getPropertyValue(
key: String, key: String,
inherit: Boolean = false, inherit: Boolean = false,
includeStyles: Boolean = true, includeStyles: Boolean = true,
includeDefaults: 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 * 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?) { public fun Vision.setProperty(name: Name, item: Any?) {
when (item) { when (item) {
null -> setPropertyNode(name, null) null -> meta.remove(name)
is Meta -> setPropertyNode(name, item) is Meta -> meta.setMeta(name, item)
is Value -> setPropertyValue(name, item) is Value -> meta.setValue(name, item)
else -> meta.setValue(name, Value.of(item))
} }
} }
public fun Vision.setPropertyNode(key: String, item: Any?) { public fun Vision.setPropertyNode(key: String, item: Any?) {
setProperty(Name.parse(key), item) 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())

View File

@ -1,24 +1,27 @@
package space.kscience.visionforge 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.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient import kotlinx.serialization.Transient
import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.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.MetaDescriptor
import space.kscience.dataforge.meta.descriptors.defaultNode import space.kscience.dataforge.meta.descriptors.defaultNode
import space.kscience.dataforge.meta.descriptors.value import space.kscience.dataforge.meta.descriptors.value
import space.kscience.dataforge.meta.get
import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.*
import space.kscience.dataforge.names.asName
import space.kscience.dataforge.names.plus
import space.kscience.dataforge.values.Value import space.kscience.dataforge.values.Value
import space.kscience.dataforge.values.ValueType import space.kscience.dataforge.values.ValueType
import space.kscience.visionforge.Vision.Companion.STYLE_KEY import space.kscience.visionforge.Vision.Companion.STYLE_KEY
import kotlin.jvm.Synchronized import kotlin.jvm.Synchronized
internal data class MetaListener(
val owner: Any? = null,
val callback: Meta.(name: Name) -> Unit,
)
/** /**
* A full base implementation for a [Vision] * A full base implementation for a [Vision]
* @param properties Object own properties excluding styles and inheritance * @param properties Object own properties excluding styles and inheritance
@ -27,12 +30,9 @@ import kotlin.jvm.Synchronized
@SerialName("vision") @SerialName("vision")
public open class VisionBase( public open class VisionBase(
@Transient override var parent: VisionGroup? = null, @Transient override var parent: VisionGroup? = null,
@Serializable(MutableMetaSerializer::class)
protected var properties: MutableMeta? = null protected var properties: MutableMeta? = null
) : Vision { ) : Vision {
//protected val observableProperties: ObservableMutableMeta by lazy { properties.asObservable() }
@Synchronized @Synchronized
protected fun getOrCreateProperties(): MutableMeta { protected fun getOrCreateProperties(): MutableMeta {
if (properties == null) { if (properties == null) {
@ -42,82 +42,105 @@ public open class VisionBase(
return properties!! return properties!!
} }
/** @Transient
* A fast accessor method to get own property (no inheritance or styles private val listeners = HashSet<MetaListener>()
*/
override fun getOwnProperty(name: Name): Meta? = properties?.getMeta(name)
override fun getProperty( private inner class VisionBaseProperties(val rootName: Name) : ObservableMutableMeta {
override val items: Map<NameToken, ObservableMutableMeta>
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, name: Name,
inherit: Boolean, inherit: Boolean,
includeStyles: Boolean, includeStyles: Boolean,
includeDefaults: Boolean, includeDefaults: Boolean,
): Meta? = if (!inherit && !includeStyles && !includeDefaults) { ): Value? {
getOwnProperty(name) properties?.get(name)?.value?.let { return it }
} else { if (includeStyles) {
buildList { getStyleProperty(name)?.let { return it }
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)
}
} }
} if (inherit) {
parent?.getPropertyValue(name, inherit, includeStyles, includeDefaults)?.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 (includeDefaults) {
descriptor?.defaultNode?.get(name)?.value.let { return it }
}
return null
} }
override val descriptor: MetaDescriptor? get() = null override val descriptor: MetaDescriptor? get() = null
private suspend fun updateStyles(names: List<String>) {
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<Name> = MutableSharedFlow()
@DFExperimental
override val propertyChanges: Flow<Name>
get() = propertyInvalidationFlow
override fun invalidateProperty(propertyName: Name) { override fun invalidateProperty(propertyName: Name) {
launch { if (propertyName == STYLE_KEY) {
if (propertyName == STYLE_KEY) { styles.mapNotNull { getStyle(it) }.asSequence()
updateStyles(styles) .flatMap { it.items.asSequence() }
} .distinctBy { it.key }
propertyInvalidationFlow.emit(propertyName) .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 { change.properties?.let {
updateProperties(Name.EMPTY, it) updateProperties(Name.EMPTY, it)
} }
@ -131,7 +154,7 @@ public open class VisionBase(
} }
public fun Vision.updateProperties(at: Name, item: Meta) { public fun Vision.updateProperties(at: Name, item: Meta) {
setPropertyValue(at, item.value) meta.setValue(at, item.value)
item.items.forEach { (token, item) -> item.items.forEach { (token, item) ->
updateProperties(at + token, item) updateProperties(at + token, item)
} }

View File

@ -89,8 +89,8 @@ private fun CoroutineScope.collectChange(
) { ) {
//Collect properties change //Collect properties change
source.onPropertyChange(this) { propertyName -> source.onPropertyChange { propertyName ->
val newItem = source.getOwnProperty(propertyName) val newItem = source.meta[propertyName]
collector().propertyChanged(name, propertyName, newItem) collector().propertyChanged(name, propertyName, newItem)
} }

View File

@ -1,5 +1,6 @@
package space.kscience.visionforge package space.kscience.visionforge
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -8,7 +9,6 @@ import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient import kotlinx.serialization.Transient
import space.kscience.dataforge.names.* import space.kscience.dataforge.names.*
/** /**
* Abstract implementation of mutable group of [Vision] * Abstract implementation of mutable group of [Vision]
* *
@ -48,7 +48,7 @@ public open class VisionGroupBase(
* Propagate children change event upwards * Propagate children change event upwards
*/ */
private fun childrenChanged(name: NameToken, before: Vision?, after: Vision?) { private fun childrenChanged(name: NameToken, before: Vision?, after: Vision?) {
launch { (manager?.context?: GlobalScope).launch {
_structureChanges.emit(MutableVisionGroup.StructureChange(name, before, after)) _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) -> change.children?.forEach { (name, change) ->
when { when {
change.delete -> set(name, null) change.delete -> set(name, null)
change.vision != null -> set(name, change.vision) change.vision != null -> set(name, change.vision)
else -> get(name)?.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 * 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) parent = RootVisionGroup(manager)
} }

View File

@ -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<NameToken, MutableMeta>
get() = vision.getProperty(rootName, inherit, includeStyles, includeDefaults)?.items?.mapValues {
VisionProperties(vision, rootName + it.key, inherit, includeStyles, includeDefaults)
} ?: emptyMap()
override var value: Value?
get() = vision.getProperty(rootName, inherit, includeStyles, includeDefaults)?.value
set(value) {
vision.setPropertyValue(rootName, value)
}
override fun getOrCreate(name: Name): MutableMeta = VisionProperties(vision, rootName + name)
override fun setMeta(name: Name, node: Meta?) {
vision.setPropertyNode(rootName + name, node)
}
override fun equals(other: Any?): Boolean = Meta.equals(this, other as? Meta)
override fun hashCode(): Int = Meta.hashCode(this)
override fun toString(): String = Meta.toString(this)
}

View File

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

View File

@ -2,9 +2,7 @@ package space.kscience.visionforge
import space.kscience.dataforge.meta.Laminate import space.kscience.dataforge.meta.Laminate
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.boolean
import space.kscience.dataforge.meta.isLeaf import space.kscience.dataforge.meta.isLeaf
import space.kscience.dataforge.values.asValue
@DslMarker @DslMarker
public annotation class VisionBuilder public annotation class VisionBuilder
@ -17,10 +15,3 @@ public fun List<Meta?>.merge(): Meta? {
else -> Laminate(filterNotNull()) //merge nodes if first encountered node is meta 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())

View File

@ -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 <S : Scheme, reified T> MetaDescriptorBuilder.value(
property: KProperty1<S, T>,
noinline block: MetaDescriptorBuilder.() -> Unit = {},
) {
when (typeOf<T>()) {
typeOf<Number>(), typeOf<Int>(), typeOf<Double>(), typeOf<Short>(), typeOf<Long>(), typeOf<Float>() ->
value(property.name, ValueType.NUMBER) {
block()
}
typeOf<Number?>(), typeOf<Int?>(), typeOf<Double?>(), typeOf<Short?>(), typeOf<Long?>(), typeOf<Float?>() ->
value(property.name, ValueType.NUMBER) {
block()
}
typeOf<Boolean>() -> value(property.name, ValueType.BOOLEAN) {
block()
}
typeOf<List<Number>>(), typeOf<List<Int>>(), typeOf<List<Double>>(), typeOf<List<Short>>(), typeOf<List<Long>>(), typeOf<List<Float>>(),
typeOf<IntArray>(), typeOf<DoubleArray>(), typeOf<ShortArray>(), typeOf<LongArray>(), typeOf<FloatArray>(),
-> value(property.name, ValueType.NUMBER) {
multiple = true
block()
}
typeOf<String>() -> value(property.name, ValueType.STRING) {
block()
}
typeOf<List<String>>(), typeOf<Array<String>>() -> 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 <S : Scheme, reified T : Scheme> MetaDescriptorBuilder.scheme(
property: KProperty1<S, T>,
spec: SchemeSpec<T>,
noinline block: MetaDescriptorBuilder.() -> Unit = {},
) {
item(property.name, spec, block)
}

View File

@ -1,45 +1,43 @@
package space.kscience.visionforge 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.names.Name
import space.kscience.dataforge.values.Value import space.kscience.dataforge.values.Value
import space.kscience.dataforge.values.number import space.kscience.dataforge.values.number
import kotlin.properties.ReadWriteProperty import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
public fun Vision.propertyNode( //public fun Vision.propertyNode(
name: Name? = null, // name: Name? = null,
inherit: Boolean = false, // inherit: Boolean = false,
includeStyles: Boolean = true, // includeStyles: Boolean = true,
includeDefaults: Boolean = true, // includeDefaults: Boolean = true,
): ReadWriteProperty<Any?, Meta?> = object : ReadWriteProperty<Any?, Meta?> { //): ReadWriteProperty<Any?, Meta?> = object : ReadWriteProperty<Any?, Meta?> {
override fun getValue(thisRef: Any?, property: KProperty<*>): Meta? = // override fun getValue(thisRef: Any?, property: KProperty<*>): Meta? =
getProperty(name ?: Name.parse(property.name), inherit, includeStyles, includeDefaults) // getProperty(name ?: Name.parse(property.name), inherit, includeStyles, includeDefaults)
//
override fun setValue(thisRef: Any?, property: KProperty<*>, value: Meta?) { // override fun setValue(thisRef: Any?, property: KProperty<*>, value: Meta?) {
setPropertyNode(name ?: Name.parse(property.name), value) // meta.setMeta(name ?: Name.parse(property.name), value)
} // }
} //}
//
public fun <T> Vision.propertyNode( //public fun <T> Vision.propertyNode(
converter: MetaConverter<T>, // converter: MetaConverter<T>,
name: Name? = null, // name: Name? = null,
inherit: Boolean = false, // inherit: Boolean = false,
includeStyles: Boolean = true, // includeStyles: Boolean = true,
includeDefaults: Boolean = true, // includeDefaults: Boolean = true,
): ReadWriteProperty<Any?, T?> = object : ReadWriteProperty<Any?, T?> { //): ReadWriteProperty<Any?, T?> = object : ReadWriteProperty<Any?, T?> {
override fun getValue(thisRef: Any?, property: KProperty<*>): T? = getProperty( // override fun getValue(thisRef: Any?, property: KProperty<*>): T? = getProperty(
name ?: Name.parse(property.name), // name ?: Name.parse(property.name),
inherit, // inherit,
includeStyles, // includeStyles,
includeDefaults // includeDefaults
)?.let(converter::metaToObject) // )?.let(converter::metaToObject)
//
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) { // override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
setPropertyNode(name ?: Name.parse(property.name), value?.let(converter::objectToMeta)) // meta.setMeta(name ?: Name.parse(property.name), value?.let(converter::objectToMeta))
} // }
} //}
public fun Vision.propertyValue( public fun Vision.propertyValue(
name: Name? = null, name: Name? = null,
@ -48,10 +46,10 @@ public fun Vision.propertyValue(
includeDefaults: Boolean = true, includeDefaults: Boolean = true,
): ReadWriteProperty<Any?, Value?> = object : ReadWriteProperty<Any?, Value?> { ): ReadWriteProperty<Any?, Value?> = object : ReadWriteProperty<Any?, Value?> {
override fun getValue(thisRef: Any?, property: KProperty<*>): Value? = 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?) { 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 <T> Vision.propertyValue(
setter: (T) -> Value? = { it?.let(Value::of) }, setter: (T) -> Value? = { it?.let(Value::of) },
getter: (Value?) -> T, getter: (Value?) -> T,
): ReadWriteProperty<Any?, T> = object : ReadWriteProperty<Any?, T> { ): ReadWriteProperty<Any?, T> = object : ReadWriteProperty<Any?, T> {
override fun getValue(thisRef: Any?, property: KProperty<*>): T = getProperty( override fun getValue(thisRef: Any?, property: KProperty<*>): T = getPropertyValue(
name ?: Name.parse(property.name), name ?: Name.parse(property.name),
inherit, inherit,
includeStyles, includeStyles,
includeDefaults includeDefaults
)?.value.let(getter) ).let(getter)
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { 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))
} }
} }

View File

@ -2,7 +2,6 @@ package space.kscience.visionforge
import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.*
import space.kscience.dataforge.meta.descriptors.* import space.kscience.dataforge.meta.descriptors.*
import space.kscience.dataforge.names.asName
import space.kscience.dataforge.values.asValue import space.kscience.dataforge.values.asValue
private const val INHERITED_DESCRIPTOR_ATTRIBUTE = "inherited" private const val INHERITED_DESCRIPTOR_ATTRIBUTE = "inherited"
@ -23,14 +22,6 @@ public var MetaDescriptorBuilder.usesStyles: Boolean
get() = attributes[STYLE_DESCRIPTOR_ATTRIBUTE].boolean ?: true get() = attributes[STYLE_DESCRIPTOR_ATTRIBUTE].boolean ?: true
set(value) = attributes.set(STYLE_DESCRIPTOR_ATTRIBUTE, value) 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 public val MetaDescriptor.widget: Meta
get() = attributes["widget"] ?: Meta.EMPTY get() = attributes["widget"] ?: Meta.EMPTY

View File

@ -5,10 +5,17 @@ import kotlinx.html.stream.createHTML
import space.kscience.dataforge.context.Global import space.kscience.dataforge.context.Global
import space.kscience.dataforge.context.fetch import space.kscience.dataforge.context.fetch
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.configure
import space.kscience.dataforge.meta.set import space.kscience.dataforge.meta.set
import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.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 import kotlin.test.Test
typealias HtmlVisionRenderer = FlowContent.(name: Name, vision: Vision, meta: Meta) -> Unit typealias HtmlVisionRenderer = FlowContent.(name: Name, vision: Vision, meta: Meta) -> Unit
@ -57,7 +64,7 @@ class HtmlTagTest {
div { div {
h2 { +"Properties" } h2 { +"Properties" }
ul { ul {
(vision as? VisionBase)?.meta()?.items?.forEach { (vision as? VisionBase)?.meta?.items?.forEach {
li { li {
a { +it.key.toString() } a { +it.key.toString() }
p { +it.value.toString() } p { +it.value.toString() }

View File

@ -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>(::TestScheme)
}
@Test
fun testPropertyUpdate(){
val vision = VisionBase()
vision.meta.getOrCreate("fff").updateWith(TestScheme){
ddd = 2
}
assertEquals(2, vision.meta["fff.ddd"]?.int)
}
}

View File

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

View File

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

View File

@ -5,6 +5,7 @@
*/ */
package space.kscience.visionforge.editor package space.kscience.visionforge.editor
import javafx.beans.property.SimpleStringProperty
import javafx.scene.control.* import javafx.scene.control.*
import javafx.scene.control.cell.TextFieldTreeTableCell import javafx.scene.control.cell.TextFieldTreeTableCell
import javafx.scene.layout.BorderPane import javafx.scene.layout.BorderPane
@ -13,6 +14,7 @@ import javafx.scene.text.Text
import space.kscience.dataforge.context.Global import space.kscience.dataforge.context.Global
import space.kscience.dataforge.meta.MutableMeta import space.kscience.dataforge.meta.MutableMeta
import space.kscience.dataforge.meta.descriptors.MetaDescriptor import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.meta.remove
import space.kscience.visionforge.dfIconView import space.kscience.visionforge.dfIconView
import tornadofx.* import tornadofx.*
@ -32,8 +34,7 @@ public class MutableMetaEditor(
MutableMeta: MutableMeta, MutableMeta: MutableMeta,
descriptor: MetaDescriptor?, descriptor: MetaDescriptor?,
title: String = "Configuration editor" 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 { override val root: BorderPane = borderpane {
center = treetableview<FXMetaModel<MutableMeta>> { center = treetableview<FXMetaModel<MutableMeta>> {
@ -64,7 +65,7 @@ public class MutableMetaEditor(
contextmenu { contextmenu {
item("Remove") { item("Remove") {
action { action {
content.remove() content.root.remove(content.nodeName)
} }
} }
} }
@ -83,7 +84,7 @@ public class MutableMetaEditor(
} }
column("Description") { param: TreeTableColumn.CellDataFeatures<FXMetaModel<MutableMeta>, String> -> column("Description") { param: TreeTableColumn.CellDataFeatures<FXMetaModel<MutableMeta>, String> ->
(param.value.value.descriptor?.info ?: "").observable() SimpleStringProperty(param.value.value.descriptor?.info ?: "")
}.setCellFactory { param: TreeTableColumn<FXMetaModel<MutableMeta>, String> -> }.setCellFactory { param: TreeTableColumn<FXMetaModel<MutableMeta>, String> ->
val cell = TreeTableCell<FXMetaModel<MutableMeta>, String>() val cell = TreeTableCell<FXMetaModel<MutableMeta>, String>()
val text = Text() val text = Text()
@ -126,51 +127,10 @@ public class MutableMetaEditor(
Global, Global,
item.valueProperty, item.valueProperty,
item.descriptor item.descriptor
) { ) { value ->
item.setValue(it) item.root.setValue(item.nodeName, value)
} }
graphic = chooser.node graphic = chooser.node
// when (item) {
// is FXMetaValue<MutableMeta> -> {
// text = null
// val chooser = ValueChooser.build(
// Global,
// item.valueProperty,
// item.descriptor
// ) {
// item.set(it)
// }
// graphic = chooser.node
// }
// is FXMetaNode<MutableMeta> -> {
// if (allowNew) {
// text = null
// graphic = HBox().apply {
// val glyph: Node = FontAwesomeIconView(FontAwesomeIcon.PLUS_CIRCLE)
// button("node", graphic = glyph) {
// hgrow = Priority.ALWAYS
// maxWidth = Double.POSITIVE_INFINITY
// action {
// showNodeDialog()?.let {
// item.addNode(it)
// }
// }
// }
// button("value", graphic = FontAwesomeIconView(FontAwesomeIcon.PLUS_SQUARE)) {
// hgrow = Priority.ALWAYS
// maxWidth = Double.POSITIVE_INFINITY
// action {
// showValueDialog()?.let {
// item.addValue(it)
// }
// }
// }
// }
// } else {
// text = ""
// }
// }
// }
} else { } else {
text = null text = null

View File

@ -5,41 +5,22 @@ import javafx.beans.property.SimpleObjectProperty
import javafx.scene.Node import javafx.scene.Node
import javafx.scene.Parent import javafx.scene.Parent
import javafx.scene.layout.VBox import javafx.scene.layout.VBox
import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.ObservableMutableMeta
import space.kscience.dataforge.meta.descriptors.MetaDescriptor 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.* 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<Vision> = SimpleObjectProperty<Vision>() public val visionProperty: SimpleObjectProperty<Vision> = SimpleObjectProperty<Vision>()
public var item: Vision? by itemProperty public var vision: Vision? by visionProperty
public val descriptorProperty: SimpleObjectProperty<MetaDescriptor> = SimpleObjectProperty<MetaDescriptor>() public val descriptorProperty: SimpleObjectProperty<MetaDescriptor> = SimpleObjectProperty<MetaDescriptor>()
public constructor( private val configProperty: Binding<ObservableMutableMeta?> = visionProperty.objectBinding { vision ->
item: Vision?, vision?.let(selector)
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<ObservableMutableMeta?> = 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 configEditorProperty: Binding<Node?> = configProperty.objectBinding(descriptorProperty) { private val configEditorProperty: Binding<Node?> = configProperty.objectBinding(descriptorProperty) {
@ -50,8 +31,8 @@ public class VisionEditorFragment(public val selector: (Vision) -> Meta) : Fragm
private val styleBoxProperty: Binding<Node?> = configProperty.objectBinding() { private val styleBoxProperty: Binding<Node?> = configProperty.objectBinding() {
VBox().apply { VBox().apply {
item?.styles?.forEach { styleName -> vision?.styles?.forEach { styleName ->
val styleMeta = item?.getStyle(styleName) val styleMeta = vision?.getStyle(styleName)
if (styleMeta != null) { if (styleMeta != null) {
titledpane(styleName, node = MetaViewer(styleMeta).root) titledpane(styleName, node = MetaViewer(styleMeta).root)
} }
@ -64,7 +45,7 @@ public class VisionEditorFragment(public val selector: (Vision) -> Meta) : Fragm
contentProperty().bind(configEditorProperty) contentProperty().bind(configEditorProperty)
} }
titledpane("Styles", collapsible = false) { titledpane("Styles", collapsible = false) {
visibleWhen(itemProperty.booleanBinding { it?.styles?.isNotEmpty() ?: false }) visibleWhen(visionProperty.booleanBinding { it?.styles?.isNotEmpty() ?: false })
contentProperty().bind(styleBoxProperty) contentProperty().bind(styleBoxProperty)
} }
} }

View File

@ -16,6 +16,7 @@ import space.kscience.dataforge.context.*
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.boolean import space.kscience.dataforge.meta.boolean
import space.kscience.dataforge.misc.Type import space.kscience.dataforge.misc.Type
import space.kscience.visionforge.computePropertyNode
import space.kscience.visionforge.solid.FX3DFactory.Companion.TYPE 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_KEY
import space.kscience.visionforge.solid.SolidMaterial.Companion.MATERIAL_WIREFRAME_KEY import space.kscience.visionforge.solid.SolidMaterial.Companion.MATERIAL_WIREFRAME_KEY
@ -73,7 +74,7 @@ class FX3DPlugin : AbstractPlugin() {
is PolyLine -> PolyLine3D( is PolyLine -> PolyLine3D(
obj.points.map { Point3D(it.x, it.y, it.z) }, obj.points.map { Point3D(it.x, it.y, it.z) },
obj.thickness.toFloat(), obj.thickness.toFloat(),
obj.getProperty(SolidMaterial.MATERIAL_COLOR_KEY, inherit = true)?.color() obj.computePropertyNode(SolidMaterial.MATERIAL_COLOR_KEY)?.color()
).apply { ).apply {
this.meshView.cullFace = CullFace.FRONT this.meshView.cullFace = CullFace.FRONT
} }

View File

@ -17,7 +17,7 @@ class FXReferenceFactory(val plugin: FX3DPlugin) : FX3DFactory<SolidReferenceGro
val prototype = obj.prototype val prototype = obj.prototype
val node = plugin.buildNode(prototype) val node = plugin.buildNode(prototype)
obj.onPropertyChange(plugin.context) { name-> obj.onPropertyChange { name->
if (name.firstOrNull()?.body == SolidReferenceGroup.REFERENCE_CHILD_PROPERTY_PREFIX) { 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 childName = name.firstOrNull()?.index?.let(Name::parse) ?: error("Wrong syntax for reference child property: '$name'")
val propertyName = name.cutFirst() val propertyName = name.cutFirst()

View File

@ -7,6 +7,7 @@ import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.startsWith import space.kscience.dataforge.names.startsWith
import space.kscience.dataforge.values.Value import space.kscience.dataforge.values.Value
import space.kscience.visionforge.Vision import space.kscience.visionforge.Vision
import space.kscience.visionforge.computePropertyNode
import space.kscience.visionforge.onPropertyChange import space.kscience.visionforge.onPropertyChange
import tornadofx.* import tornadofx.*
@ -17,7 +18,7 @@ public class VisualObjectFXBinding(public val fx: FX3DPlugin, public val obj: Vi
private val bindings = HashMap<Name, ObjectBinding<Meta?>>() private val bindings = HashMap<Name, ObjectBinding<Meta?>>()
init { init {
obj.onPropertyChange(fx.context) { name -> obj.onPropertyChange { name ->
bindings.filter { it.key.startsWith(name) }.forEach { entry -> bindings.filter { it.key.startsWith(name) }.forEach { entry ->
Platform.runLater { Platform.runLater {
entry.value.invalidate() 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<Meta?> { public operator fun get(key: Name): ObjectBinding<Meta?> {
return bindings.getOrPut(key) { return bindings.getOrPut(key) {
object : ObjectBinding<Meta?>() { object : ObjectBinding<Meta?>() {
override fun computeValue(): Meta? = obj.getProperty(key) override fun computeValue(): Meta? = obj.computePropertyNode(key)
} }
} }
} }

View File

@ -21,7 +21,7 @@ public class VisionOfMarkup(
//TODO add templates //TODO add templates
public var content: String? public var content: String?
get() = getOwnProperty(CONTENT_PROPERTY_KEY).string get() = meta.getMeta(CONTENT_PROPERTY_KEY).string
set(value) { set(value) {
setProperty(CONTENT_PROPERTY_KEY, value) setProperty(CONTENT_PROPERTY_KEY, value)
} }

View File

@ -2,8 +2,6 @@ package space.kscience.visionforge.plotly
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import space.kscience.dataforge.meta.MutableMeta
import space.kscience.dataforge.meta.asObservable
import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.misc.DFExperimental
import space.kscience.plotly.Plot import space.kscience.plotly.Plot
import space.kscience.plotly.Plotly import space.kscience.plotly.Plotly
@ -18,7 +16,7 @@ public class VisionOfPlotly private constructor() : VisionBase() {
properties = plot.meta 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) public fun Plot.asVision(): VisionOfPlotly = VisionOfPlotly(this)

View File

@ -137,7 +137,7 @@ public class VisionServer internal constructor(
val change = visionManager.jsonFormat.decodeFromString( val change = visionManager.jsonFormat.decodeFromString(
VisionChange.serializer(), it.data.decodeToString() VisionChange.serializer(), it.data.decodeToString()
) )
vision.change(change) vision.update(change)
} }
} }

View File

@ -6,20 +6,23 @@ import space.kscience.dataforge.values.Value
import space.kscience.dataforge.values.asValue import space.kscience.dataforge.values.asValue
import space.kscience.dataforge.values.string import space.kscience.dataforge.values.string
import space.kscience.visionforge.Colors import space.kscience.visionforge.Colors
import space.kscience.visionforge.Vision
import space.kscience.visionforge.VisionBuilder import space.kscience.visionforge.VisionBuilder
import space.kscience.visionforge.VisionPropertyContainer
import kotlin.jvm.JvmInline
@VisionBuilder @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? public var value: Value?
get() = parent.getMeta(colorKey)?.value get() = parent().getMeta(colorKey)?.value
set(value) { set(value) {
parent.setValue(colorKey,value) parent().setValue(colorKey,value)
} }
public var item: Meta? public var item: Meta?
get() = parent.getMeta(colorKey) get() = parent().getMeta(colorKey)
set(value) { set(value) {
parent.setMeta(colorKey,value) parent().setMeta(colorKey,value)
} }
} }

View File

@ -2,6 +2,7 @@ package space.kscience.visionforge.solid
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import space.kscience.dataforge.meta.configure
import space.kscience.dataforge.meta.update import space.kscience.dataforge.meta.update
import space.kscience.visionforge.* import space.kscience.visionforge.*
@ -29,20 +30,20 @@ public inline fun VisionContainerBuilder<Solid>.composite(
val group = SolidGroup().apply(builder) val group = SolidGroup().apply(builder)
val children = group.children.values.filterIsInstance<Solid>() val children = group.children.values.filterIsInstance<Solid>()
if (children.size != 2) error("Composite requires exactly two children") if (children.size != 2) error("Composite requires exactly two children")
return Composite(type, children[0], children[1]).also { composite -> return Composite(type, children[0], children[1]).apply {
composite.configure { configure {
update(group.meta()) update(group.meta)
} }
if (group.position != null) { if (group.position != null) {
composite.position = group.position position = group.position
} }
if (group.rotation != null) { if (group.rotation != null) {
composite.rotation = group.rotation rotation = group.rotation
} }
if (group.scale != null) { if (group.scale != null) {
composite.scale = group.scale scale = group.scale
} }
set(name, composite) set(name, this)
} }
} }

View File

@ -4,6 +4,7 @@ import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import space.kscience.dataforge.meta.MutableMeta import space.kscience.dataforge.meta.MutableMeta
import space.kscience.dataforge.meta.ObservableMutableMeta import space.kscience.dataforge.meta.ObservableMutableMeta
import space.kscience.dataforge.meta.configure
import space.kscience.visionforge.* import space.kscience.visionforge.*
import kotlin.math.PI import kotlin.math.PI
import kotlin.math.cos import kotlin.math.cos
@ -106,7 +107,9 @@ public class ExtrudeBuilder(
layers.add(Layer(x.toFloat(), y.toFloat(), z.toFloat(), scale.toFloat())) layers.add(Layer(x.toFloat(), y.toFloat(), z.toFloat(), scale.toFloat()))
} }
internal fun build(): Extruded = Extruded(shape, layers).apply { configure(meta()) } internal fun build(): Extruded = Extruded(shape, layers).apply {
configure(this@ExtrudeBuilder.meta)
}
} }
@VisionBuilder @VisionBuilder

View File

@ -5,8 +5,7 @@ import space.kscience.dataforge.meta.descriptors.*
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.asName
import space.kscience.dataforge.names.plus import space.kscience.dataforge.names.plus
import space.kscience.dataforge.values.ValueType import space.kscience.dataforge.values.*
import space.kscience.dataforge.values.asValue
import space.kscience.visionforge.* import space.kscience.visionforge.*
import space.kscience.visionforge.Vision.Companion.VISIBLE_KEY import space.kscience.visionforge.Vision.Companion.VISIBLE_KEY
import space.kscience.visionforge.solid.Solid.Companion.DETAIL_KEY import space.kscience.visionforge.solid.Solid.Companion.DETAIL_KEY
@ -76,6 +75,8 @@ public interface Solid : Vision {
default(true) default(true)
} }
node(SolidMaterial.MATERIAL_KEY.toString(), SolidMaterial)
//TODO replace by descriptor merge //TODO replace by descriptor merge
value(Vision.STYLE_KEY, ValueType.STRING) { value(Vision.STYLE_KEY, ValueType.STRING) {
multiple = true multiple = true
@ -98,10 +99,6 @@ public interface Solid : Vision {
hide() hide()
} }
item(SolidMaterial.MATERIAL_KEY.toString(), SolidMaterial){
valueRequirement = ValueRequirement.ABSENT
}
enum(ROTATION_ORDER_KEY, default = RotationOrder.XYZ) { enum(ROTATION_ORDER_KEY, default = RotationOrder.XYZ) {
hide() hide()
} }
@ -114,7 +111,7 @@ public interface Solid : Vision {
* Get the layer number this solid belongs to. Return 0 if layer is not defined. * Get the layer number this solid belongs to. Return 0 if layer is not defined.
*/ */
public var Solid.layer: Int public var Solid.layer: Int
get() = getProperty(LAYER_KEY, inherit = true).int ?: 0 get() = getPropertyValue(LAYER_KEY, inherit = true)?.int ?: 0
set(value) { set(value) {
setProperty(LAYER_KEY, value) setProperty(LAYER_KEY, value)
} }
@ -134,24 +131,24 @@ public enum class RotationOrder {
* Rotation order * Rotation order
*/ */
public var Solid.rotationOrder: RotationOrder public var Solid.rotationOrder: RotationOrder
get() = getProperty(Solid.ROTATION_ORDER_KEY).enum<RotationOrder>() ?: RotationOrder.XYZ get() = getPropertyValue(Solid.ROTATION_ORDER_KEY)?.enum<RotationOrder>() ?: RotationOrder.XYZ
set(value) = setPropertyValue(Solid.ROTATION_ORDER_KEY, value.name.asValue()) 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 * Preferred number of polygons for displaying the object. If not defined, uses shape or renderer default. Not inherited
*/ */
public var Solid.detail: Int? public var Solid.detail: Int?
get() = getProperty(DETAIL_KEY, false).int get() = getPropertyValue(DETAIL_KEY, false)?.int
set(value) = setPropertyValue(DETAIL_KEY, value?.asValue()) set(value) = meta.setValue(DETAIL_KEY, value?.asValue())
/** /**
* If this property is true, the object will be ignored on render. * If this property is true, the object will be ignored on render.
* Property is not inherited. * Property is not inherited.
*/ */
public var Vision.ignore: Boolean? public var Vision.ignore: Boolean?
get() = getProperty(IGNORE_KEY, false).boolean get() = getPropertyValue(IGNORE_KEY, false)?.boolean
set(value) = setPropertyValue(IGNORE_KEY, value?.asValue()) set(value) = meta.setValue(IGNORE_KEY, value?.asValue())
//var VisualObject.selected: Boolean? //var VisualObject.selected: Boolean?
// get() = getProperty(SELECTED_KEY).boolean // get() = getProperty(SELECTED_KEY).boolean
@ -160,7 +157,7 @@ public var Vision.ignore: Boolean?
internal fun float(name: Name, default: Number): ReadWriteProperty<Solid, Number> = internal fun float(name: Name, default: Number): ReadWriteProperty<Solid, Number> =
object : ReadWriteProperty<Solid, Number> { object : ReadWriteProperty<Solid, Number> {
override fun getValue(thisRef: Solid, property: KProperty<*>): Number { 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) { override fun setValue(thisRef: Solid, property: KProperty<*>, value: Number) {
@ -171,7 +168,7 @@ internal fun float(name: Name, default: Number): ReadWriteProperty<Solid, Number
internal fun point(name: Name, default: Float): ReadWriteProperty<Solid, Point3D?> = internal fun point(name: Name, default: Float): ReadWriteProperty<Solid, Point3D?> =
object : ReadWriteProperty<Solid, Point3D?> { object : ReadWriteProperty<Solid, Point3D?> {
override fun getValue(thisRef: Solid, property: KProperty<*>): Point3D? { 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 { return object : Point3D {
override val x: Float get() = item[X_KEY]?.float ?: default override val x: Float get() = item[X_KEY]?.float ?: default
override val y: Float get() = item[Y_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<Solid, Point3D
override fun setValue(thisRef: Solid, property: KProperty<*>, value: Point3D?) { override fun setValue(thisRef: Solid, property: KProperty<*>, value: Point3D?) {
if (value == null) { if (value == null) {
thisRef.setPropertyNode(name, null) thisRef.meta.setMeta(name, null)
} else { } else {
thisRef.setProperty(name + X_KEY, value.x) thisRef.setProperty(name + X_KEY, value.x)
thisRef.setProperty(name + Y_KEY, value.y) thisRef.setProperty(name + Y_KEY, value.y)

View File

@ -11,8 +11,8 @@ import space.kscience.visionforge.VisionChange
public open class SolidBase : VisionBase(), Solid { public open class SolidBase : VisionBase(), Solid {
override val descriptor: MetaDescriptor get() = Solid.descriptor override val descriptor: MetaDescriptor get() = Solid.descriptor
override fun change(change: VisionChange) { override fun update(change: VisionChange) {
updatePosition(change.properties) updatePosition(change.properties)
super.change(change) super.update(change)
} }
} }

View File

@ -60,9 +60,9 @@ public class SolidGroup : VisionGroupBase(), Solid, PrototypeHolder {
override fun createGroup(): SolidGroup = SolidGroup() override fun createGroup(): SolidGroup = SolidGroup()
override fun change(change: VisionChange) { override fun update(change: VisionChange) {
updatePosition(change.properties) updatePosition(change.properties)
super.change(change) super.update(change)
} }
public companion object { public companion object {

View File

@ -8,6 +8,7 @@ import space.kscience.dataforge.names.asName
import space.kscience.dataforge.names.plus import space.kscience.dataforge.names.plus
import space.kscience.dataforge.values.ValueType import space.kscience.dataforge.values.ValueType
import space.kscience.dataforge.values.asValue import space.kscience.dataforge.values.asValue
import space.kscience.dataforge.values.number
import space.kscience.visionforge.* import space.kscience.visionforge.*
import space.kscience.visionforge.solid.SolidMaterial.Companion.MATERIAL_COLOR_KEY import space.kscience.visionforge.solid.SolidMaterial.Companion.MATERIAL_COLOR_KEY
import space.kscience.visionforge.solid.SolidMaterial.Companion.MATERIAL_KEY import space.kscience.visionforge.solid.SolidMaterial.Companion.MATERIAL_KEY
@ -19,14 +20,14 @@ public class SolidMaterial : Scheme() {
/** /**
* Primary web-color for the material * 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 * 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 * Opacity
@ -89,24 +90,19 @@ public class SolidMaterial : Scheme() {
} }
public val Solid.color: ColorAccessor public val Solid.color: ColorAccessor
get() = ColorAccessor( get() = ColorAccessor(MATERIAL_COLOR_KEY) { computeProperties() }
meta(inherit = true),
MATERIAL_COLOR_KEY
)
public var Solid.material: SolidMaterial? public var Solid.material: SolidMaterial?
get() = getProperty(MATERIAL_KEY, inherit = true)?.let { SolidMaterial.read(it) } get() = computePropertyNode(MATERIAL_KEY)?.let { SolidMaterial.read(it) }
set(value) = setPropertyNode(MATERIAL_KEY, value?.meta) set(value) = meta.setMeta(MATERIAL_KEY, value?.meta)
@VisionBuilder @VisionBuilder
public fun Solid.material(builder: SolidMaterial.() -> Unit) { public fun Solid.material(builder: SolidMaterial.() -> Unit) {
configure(MATERIAL_KEY){ meta.getOrCreate(MATERIAL_KEY).updateWith(SolidMaterial, builder)
updateWith(SolidMaterial,builder)
}
} }
public var Solid.opacity: Number? public var Solid.opacity: Number?
get() = getProperty(MATERIAL_OPACITY_KEY, inherit = true).number get() = getPropertyValue(MATERIAL_OPACITY_KEY, inherit = true)?.number
set(value) { set(value) {
setPropertyValue(MATERIAL_OPACITY_KEY, value?.asValue()) meta.setValue(MATERIAL_OPACITY_KEY, value?.asValue())
} }

View File

@ -1,12 +1,10 @@
package space.kscience.visionforge.solid package space.kscience.visionforge.solid
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.ObservableMutableMeta
import space.kscience.dataforge.meta.descriptors.MetaDescriptor 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.names.*
import space.kscience.dataforge.values.Value import space.kscience.dataforge.values.Value
import space.kscience.visionforge.* 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 * The prototype for this reference. Always returns a "real" prototype, not a reference
*/ */
public val prototype: Solid 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") 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 = private fun childToken(childName: Name): NameToken =
NameToken(SolidReferenceGroup.REFERENCE_CHILD_PROPERTY_PREFIX, childName.toString()) NameToken(SolidReferenceGroup.REFERENCE_CHILD_PROPERTY_PREFIX, childName.toString())
@ -83,12 +72,8 @@ public class SolidReferenceGroup(
ReferenceChild(this, it.key.asName()) ReferenceChild(this, it.key.asName())
} ?: emptyMap() } ?: emptyMap()
override fun getProperty( override fun getPropertyValue(name: Name, inherit: Boolean, includeStyles: Boolean, includeDefaults: Boolean): Value? =
name: Name, super<SolidReference>.getPropertyValue(name, inherit, includeStyles, includeDefaults)
inherit: Boolean,
includeStyles: Boolean,
includeDefaults: Boolean,
): Meta? = getRefProperty(name, inherit, includeStyles, includeDefaults)
override val descriptor: MetaDescriptor get() = prototype.descriptor 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<NameToken, Vision> override val children: Map<NameToken, Vision>
get() = (prototype as? VisionGroup)?.children get() = (prototype as? VisionGroup)?.children
?.filter { it.key != SolidGroup.PROTOTYPES_TOKEN } ?.filter { it.key != SolidGroup.PROTOTYPES_TOKEN }
@ -118,24 +107,6 @@ public class SolidReferenceGroup(
ReferenceChild(owner, refName + key.asName()) ReferenceChild(owner, refName + key.asName())
} ?: emptyMap() } ?: 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? override var parent: VisionGroup?
get() { get() {
val parentName = refName.cutLast() val parentName = refName.cutLast()
@ -145,21 +116,11 @@ public class SolidReferenceGroup(
error("Setting a parent for a reference child is not possible") error("Setting a parent for a reference child is not possible")
} }
@DFExperimental
override val propertyChanges: Flow<Name>
get() = owner.propertyChanges.mapNotNull { name ->
if (name.startsWith(childToken(refName))) {
name.cutFirst()
} else {
null
}
}
override fun invalidateProperty(propertyName: Name) { override fun invalidateProperty(propertyName: Name) {
owner.invalidateProperty(childPropertyName(refName, propertyName)) owner.invalidateProperty(childPropertyName(refName, propertyName))
} }
override fun change(change: VisionChange) { override fun update(change: VisionChange) {
change.properties?.let { change.properties?.let {
updateProperties(Name.EMPTY, it) updateProperties(Name.EMPTY, it)
} }

View File

@ -4,8 +4,8 @@ import space.kscience.dataforge.meta.Scheme
import space.kscience.dataforge.meta.SchemeSpec import space.kscience.dataforge.meta.SchemeSpec
import space.kscience.dataforge.meta.boolean import space.kscience.dataforge.meta.boolean
import space.kscience.dataforge.meta.descriptors.MetaDescriptor 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.double
import space.kscience.visionforge.value
public class Axes : Scheme() { public class Axes : Scheme() {
public var visible: Boolean by boolean(false) public var visible: Boolean by boolean(false)

View File

@ -3,9 +3,9 @@ package space.kscience.visionforge.solid.specifications
import space.kscience.dataforge.meta.Scheme import space.kscience.dataforge.meta.Scheme
import space.kscience.dataforge.meta.SchemeSpec import space.kscience.dataforge.meta.SchemeSpec
import space.kscience.dataforge.meta.descriptors.MetaDescriptor 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.double
import space.kscience.dataforge.meta.int import space.kscience.dataforge.meta.int
import space.kscience.visionforge.value
import kotlin.math.PI import kotlin.math.PI
public class Camera : Scheme() { public class Camera : Scheme() {
@ -27,24 +27,24 @@ public class Camera : Scheme() {
public const val FAR_CLIP: Double = 10000.0 public const val FAR_CLIP: Double = 10000.0
public const val FIELD_OF_VIEW: Int = 75 public const val FIELD_OF_VIEW: Int = 75
override val descriptor: MetaDescriptor by lazy { override val descriptor: MetaDescriptor by lazy {
MetaDescriptor { MetaDescriptor {
value(Camera::fov){ value(Camera::fov) {
default(FIELD_OF_VIEW) default(FIELD_OF_VIEW)
} }
value(Camera::nearClip){ value(Camera::nearClip) {
default(NEAR_CLIP) default(NEAR_CLIP)
} }
value(Camera::farClip){ value(Camera::farClip) {
default(FAR_CLIP) default(FAR_CLIP)
} }
value(Camera::distance){ value(Camera::distance) {
default(INITIAL_DISTANCE) default(INITIAL_DISTANCE)
} }
value(Camera::azimuth){ value(Camera::azimuth) {
default(INITIAL_AZIMUTH) default(INITIAL_AZIMUTH)
} }
value(Camera::latitude){ value(Camera::latitude) {
default(INITIAL_LATITUDE) default(INITIAL_LATITUDE)
} }
} }

View File

@ -2,11 +2,11 @@ package space.kscience.visionforge.solid.specifications
import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.*
import space.kscience.dataforge.meta.descriptors.MetaDescriptor 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.names.Name
import space.kscience.dataforge.values.ValueType import space.kscience.dataforge.values.ValueType
import space.kscience.visionforge.hide import space.kscience.visionforge.hide
import space.kscience.visionforge.scheme
import space.kscience.visionforge.value
import space.kscience.visionforge.widgetType import space.kscience.visionforge.widgetType
public class Clipping : Scheme() { public class Clipping : Scheme() {
@ -80,7 +80,19 @@ public class Canvas3DOptions : Scheme() {
override val descriptor: MetaDescriptor by lazy { override val descriptor: MetaDescriptor by lazy {
MetaDescriptor { MetaDescriptor {
scheme(Canvas3DOptions::axes, Axes) 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) { scheme(Canvas3DOptions::camera, Camera) {
hide() hide()
@ -93,15 +105,6 @@ public class Canvas3DOptions : Scheme() {
scheme(Canvas3DOptions::size, CanvasSize) { scheme(Canvas3DOptions::size, CanvasSize) {
hide() 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)
} }
} }
} }

View File

@ -1,5 +1,6 @@
package space.kscience.visionforge.solid.transform package space.kscience.visionforge.solid.transform
import space.kscience.dataforge.meta.configure
import space.kscience.dataforge.meta.update import space.kscience.dataforge.meta.update
import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.asName
@ -22,7 +23,7 @@ internal fun Vision.updateFrom(other: Vision): Vision {
scaleY *= other.scaleY scaleY *= other.scaleY
scaleZ *= other.scaleZ scaleZ *= other.scaleZ
configure{ configure{
update(other.meta()) update(other.meta)
} }
} }
return this return this

View File

@ -1,13 +1,24 @@
package space.kscience.visionforge.solid 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.names.asName
import space.kscience.dataforge.values.int
import space.kscience.visionforge.* import space.kscience.visionforge.*
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
@Suppress("UNUSED_VARIABLE") @Suppress("UNUSED_VARIABLE")
class PropertyTest { 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 @Test
fun testInheritedProperty() { fun testInheritedProperty() {
var box: Box? = null var box: Box? = null
@ -17,7 +28,7 @@ class PropertyTest {
box = box(100, 100, 100) box = box(100, 100, 100)
} }
} }
assertEquals(22, box?.getProperty("test", inherit = true).int) assertEquals(22, box?.getPropertyValue("test", inherit = true)?.int)
} }
@Test @Test
@ -35,11 +46,11 @@ class PropertyTest {
} }
} }
} }
assertEquals(22, box?.getProperty("test").int) assertEquals(22, box?.getPropertyValue("test")?.int)
} }
@Test @Test
fun testColor() { fun testStyleColor() {
var box: Box? = null var box: Box? = null
val group = SolidGroup().apply { val group = SolidGroup().apply {
styleSheet { styleSheet {

View File

@ -3,7 +3,6 @@ package space.kscience.visionforge.solid
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.visionforge.MutableVisionGroup import space.kscience.visionforge.MutableVisionGroup
import space.kscience.visionforge.get import space.kscience.visionforge.get
import space.kscience.visionforge.meta
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -32,7 +31,7 @@ class SerializationTest {
val string = Solids.encodeToString(cube) val string = Solids.encodeToString(cube)
println(string) println(string)
val newCube = Solids.decodeFromString(string) val newCube = Solids.decodeFromString(string)
assertEquals(cube.meta(), newCube.meta()) assertEquals(cube.meta, newCube.meta)
} }
@Test @Test
@ -53,7 +52,7 @@ class SerializationTest {
val string = Solids.encodeToString(group) val string = Solids.encodeToString(group)
println(string) println(string)
val reconstructed = Solids.decodeFromString(string) as SolidGroup val reconstructed = Solids.decodeFromString(string) as SolidGroup
assertEquals(group["cube"]?.meta(), reconstructed["cube"]?.meta()) assertEquals(group["cube"]?.meta, reconstructed["cube"]?.meta)
} }
} }

View File

@ -28,7 +28,7 @@ class VisionUpdateTest {
propertyChanged("top".asName(), SolidMaterial.MATERIAL_COLOR_KEY, Meta("red".asValue())) propertyChanged("top".asName(), SolidMaterial.MATERIAL_COLOR_KEY, Meta("red".asValue()))
propertyChanged("origin".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 } assertTrue { targetVision["top"] is SolidGroup }
assertEquals("red", (targetVision["origin"] as Solid).color.string) // Should work assertEquals("red", (targetVision["origin"] as Solid).color.string) // Should work
assertEquals("#00007b", (targetVision["top"] as Solid).color.string) // new item always takes precedence assertEquals("#00007b", (targetVision["top"] as Solid).color.string) // new item always takes precedence

View File

@ -4,12 +4,13 @@ import info.laht.threekt.core.BufferGeometry
import info.laht.threekt.geometries.EdgesGeometry import info.laht.threekt.geometries.EdgesGeometry
import info.laht.threekt.objects.LineSegments import info.laht.threekt.objects.LineSegments
import info.laht.threekt.objects.Mesh import info.laht.threekt.objects.Mesh
import space.kscience.dataforge.meta.boolean import space.kscience.dataforge.meta.get
import space.kscience.dataforge.meta.node
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.asName
import space.kscience.dataforge.names.plus import space.kscience.dataforge.names.plus
import space.kscience.dataforge.names.startsWith 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.onPropertyChange
import space.kscience.visionforge.solid.Solid import space.kscience.visionforge.solid.Solid
import space.kscience.visionforge.solid.SolidMaterial import space.kscience.visionforge.solid.SolidMaterial
@ -40,7 +41,7 @@ public abstract class MeshThreeFactory<in T : Solid>(
} }
//add listener to object properties //add listener to object properties
obj.onPropertyChange(three.updateScope) { name -> obj.onPropertyChange { name ->
when { when {
name.startsWith(Solid.GEOMETRY_KEY) -> { name.startsWith(Solid.GEOMETRY_KEY) -> {
val oldGeometry = mesh.geometry as BufferGeometry val oldGeometry = mesh.geometry as BufferGeometry
@ -83,14 +84,10 @@ internal fun Mesh.applyProperties(obj: Solid): Mesh = apply {
public fun Mesh.applyEdges(obj: Solid) { public fun Mesh.applyEdges(obj: Solid) {
val edges = children.find { it.name == "@edges" } as? LineSegments val edges = children.find { it.name == "@edges" } as? LineSegments
//inherited edges definition, enabled by default //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 bufferGeometry = geometry as? BufferGeometry ?: return
val material = ThreeMaterials.getLineMaterial( val material = ThreeMaterials.getLineMaterial(
obj.getProperty( obj.computeProperties().get(MeshThreeFactory.EDGES_MATERIAL_KEY),
MeshThreeFactory.EDGES_MATERIAL_KEY,
inherit = true,
includeStyles = true
),
true true
) )
if (edges == null) { if (edges == null) {

View File

@ -48,7 +48,7 @@ public class ThreeCompositeFactory(public val three: ThreePlugin) : ThreeFactory
}.apply { }.apply {
updatePosition(obj) updatePosition(obj)
applyProperties(obj) applyProperties(obj)
obj.onPropertyChange(three.updateScope) { name -> obj.onPropertyChange { name ->
when { when {
//name.startsWith(WIREFRAME_KEY) -> mesh.applyWireFrame(obj) //name.startsWith(WIREFRAME_KEY) -> mesh.applyWireFrame(obj)
name.startsWith(MeshThreeFactory.EDGES_KEY) -> applyEdges(obj) name.startsWith(MeshThreeFactory.EDGES_KEY) -> applyEdges(obj)

View File

@ -40,7 +40,7 @@ public class ThreeGeometryBuilder : GeometryBuilder<BufferGeometry> {
} }
override fun face(vertex1: Point3D, vertex2: Point3D, vertex3: Point3D, normal: Point3D?, meta: Meta) { 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( indices.add(
vertex(vertex1, actualNormal), vertex(vertex1, actualNormal),
vertex(vertex2, actualNormal), vertex(vertex2, actualNormal),

View File

@ -27,7 +27,7 @@ public object ThreeLabelFactory : ThreeFactory<SolidLabel> {
return Mesh(textGeo, ThreeMaterials.DEFAULT).apply { return Mesh(textGeo, ThreeMaterials.DEFAULT).apply {
updateMaterial(obj) updateMaterial(obj)
updatePosition(obj) updatePosition(obj)
obj.onPropertyChange(three.updateScope) { _ -> obj.onPropertyChange { _ ->
//TODO //TODO
three.logger.warn { "Label parameter change not implemented" } three.logger.warn { "Label parameter change not implemented" }
} }

View File

@ -4,6 +4,7 @@ import info.laht.threekt.core.BufferGeometry
import info.laht.threekt.core.Object3D import info.laht.threekt.core.Object3D
import info.laht.threekt.math.Color import info.laht.threekt.math.Color
import info.laht.threekt.objects.LineSegments import info.laht.threekt.objects.LineSegments
import space.kscience.visionforge.computePropertyNode
import space.kscience.visionforge.onPropertyChange import space.kscience.visionforge.onPropertyChange
import space.kscience.visionforge.solid.PolyLine import space.kscience.visionforge.solid.PolyLine
import space.kscience.visionforge.solid.color import space.kscience.visionforge.solid.color
@ -20,7 +21,7 @@ public object ThreeLineFactory : ThreeFactory<PolyLine> {
} }
val material = ThreeMaterials.getLineMaterial( val material = ThreeMaterials.getLineMaterial(
obj.getProperty(MeshThreeFactory.EDGES_MATERIAL_KEY), obj.computePropertyNode(MeshThreeFactory.EDGES_MATERIAL_KEY),
true true
) )
@ -31,7 +32,7 @@ public object ThreeLineFactory : ThreeFactory<PolyLine> {
updatePosition(obj) updatePosition(obj)
//layers.enable(obj.layer) //layers.enable(obj.layer)
//add listener to object properties //add listener to object properties
obj.onPropertyChange(three.updateScope) { propertyName -> obj.onPropertyChange { propertyName ->
updateProperty(obj, propertyName) updateProperty(obj, propertyName)
} }
} }

View File

@ -8,12 +8,10 @@ import info.laht.threekt.math.Color
import info.laht.threekt.objects.Mesh import info.laht.threekt.objects.Mesh
import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.*
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.values.ValueType import space.kscience.dataforge.values.*
import space.kscience.dataforge.values.int
import space.kscience.dataforge.values.string
import space.kscience.visionforge.Colors import space.kscience.visionforge.Colors
import space.kscience.visionforge.Vision import space.kscience.visionforge.Vision
import space.kscience.visionforge.meta import space.kscience.visionforge.computePropertyNode
import space.kscience.visionforge.solid.SolidMaterial import space.kscience.visionforge.solid.SolidMaterial
@ -117,8 +115,8 @@ private var Material.cached: Boolean
public fun Mesh.updateMaterial(vision: Vision) { public fun Mesh.updateMaterial(vision: Vision) {
//val meta = vision.getProperty(SolidMaterial.MATERIAL_KEY, inherit = true).node //val meta = vision.getProperty(SolidMaterial.MATERIAL_KEY, inherit = true).node
val ownMaterialMeta = vision.meta()[SolidMaterial.MATERIAL_KEY] val ownMaterialMeta = vision.meta.getMeta(SolidMaterial.MATERIAL_KEY)
val parentMaterialMeta = vision.parent?.getProperty( val parentMaterialMeta = vision.parent?.getPropertyValue(
SolidMaterial.MATERIAL_KEY, SolidMaterial.MATERIAL_KEY,
inherit = true, inherit = true,
includeStyles = false, includeStyles = false,
@ -128,19 +126,15 @@ public fun Mesh.updateMaterial(vision: Vision) {
material = when { material = when {
ownMaterialMeta == null && parentMaterialMeta == null -> { ownMaterialMeta == null && parentMaterialMeta == null -> {
//If material is style-based, use cached //If material is style-based, use cached
vision.getProperty( vision.computePropertyNode(
SolidMaterial.MATERIAL_KEY, SolidMaterial.MATERIAL_KEY,
inherit = false,
includeStyles = true,
includeDefaults = false
)?.let { )?.let {
ThreeMaterials.cacheMaterial(it) ThreeMaterials.cacheMaterial(it)
} ?: ThreeMaterials.DEFAULT } ?: ThreeMaterials.DEFAULT
} }
else -> { else -> {
vision.getProperty( vision.computePropertyNode(
SolidMaterial.MATERIAL_KEY, SolidMaterial.MATERIAL_KEY,
inherit = true
)?.let { )?.let {
ThreeMaterials.buildMaterial(it) ThreeMaterials.buildMaterial(it)
} ?: ThreeMaterials.DEFAULT } ?: ThreeMaterials.DEFAULT
@ -155,32 +149,29 @@ public fun Mesh.updateMaterialProperty(vision: Vision, propertyName: Name) {
} else { } else {
when (propertyName) { when (propertyName) {
SolidMaterial.MATERIAL_COLOR_KEY -> { SolidMaterial.MATERIAL_COLOR_KEY -> {
material.asDynamic().color = vision.getProperty( material.asDynamic().color = vision.computePropertyNode(
SolidMaterial.MATERIAL_COLOR_KEY, SolidMaterial.MATERIAL_COLOR_KEY,
inherit = true,
includeStyles = true,
includeDefaults = false
)?.threeColor() ?: ThreeMaterials.DEFAULT_COLOR )?.threeColor() ?: ThreeMaterials.DEFAULT_COLOR
material.needsUpdate = true material.needsUpdate = true
} }
SolidMaterial.MATERIAL_OPACITY_KEY -> { SolidMaterial.MATERIAL_OPACITY_KEY -> {
val opacity = vision.getProperty( val opacity = vision.getPropertyValue(
SolidMaterial.MATERIAL_OPACITY_KEY, SolidMaterial.MATERIAL_OPACITY_KEY,
inherit = true, inherit = true,
includeStyles = true, includeStyles = true,
includeDefaults = false includeDefaults = false
).double ?: 1.0 )?.double ?: 1.0
material.opacity = opacity material.opacity = opacity
material.transparent = opacity < 1.0 material.transparent = opacity < 1.0
material.needsUpdate = true material.needsUpdate = true
} }
SolidMaterial.MATERIAL_WIREFRAME_KEY -> { SolidMaterial.MATERIAL_WIREFRAME_KEY -> {
material.asDynamic().wireframe = vision.getProperty( material.asDynamic().wireframe = vision.getPropertyValue(
SolidMaterial.MATERIAL_WIREFRAME_KEY, SolidMaterial.MATERIAL_WIREFRAME_KEY,
inherit = true, inherit = true,
includeStyles = true, includeStyles = true,
includeDefaults = false includeDefaults = false
).boolean ?: false )?.boolean ?: false
material.needsUpdate = true material.needsUpdate = true
} }
else -> console.warn("Unrecognized material property: $propertyName") else -> console.warn("Unrecognized material property: $propertyName")

View File

@ -69,7 +69,7 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
updatePosition(obj) updatePosition(obj)
//obj.onChildrenChange() //obj.onChildrenChange()
obj.onPropertyChange(updateScope) { name -> obj.onPropertyChange { name ->
if ( if (
name.startsWith(Solid.POSITION_KEY) || name.startsWith(Solid.POSITION_KEY) ||
name.startsWith(Solid.ROTATION_KEY) || name.startsWith(Solid.ROTATION_KEY) ||

View File

@ -47,7 +47,7 @@ public object ThreeReferenceFactory : ThreeFactory<SolidReferenceGroup> {
//TODO apply child properties //TODO apply child properties
obj.onPropertyChange(three.updateScope) { name-> obj.onPropertyChange { name->
if (name.firstOrNull()?.body == REFERENCE_CHILD_PROPERTY_PREFIX) { 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 childName = name.firstOrNull()?.index?.let(Name::parse) ?: error("Wrong syntax for reference child property: '$name'")
val propertyName = name.cutFirst() val propertyName = name.cutFirst()

View File

@ -3,7 +3,8 @@
"OVERRIDING_FINAL_MEMBER", "OVERRIDING_FINAL_MEMBER",
"RETURN_TYPE_MISMATCH_ON_OVERRIDE", "RETURN_TYPE_MISMATCH_ON_OVERRIDE",
"CONFLICTING_OVERLOADS", "CONFLICTING_OVERLOADS",
"EXTERNAL_DELEGATION" "EXTERNAL_DELEGATION",
"NO_EXPLICIT_VISIBILITY_IN_API_MODE_WARNING"
) )
@file:JsModule("three-csg-ts") @file:JsModule("three-csg-ts")