A lot of changes

This commit is contained in:
Alexander Nozik 2021-08-08 22:17:50 +03:00
parent 8d21d3cd74
commit 7b78052f61
58 changed files with 524 additions and 784 deletions

View File

@ -2,7 +2,7 @@ plugins {
id("ru.mipt.npm.gradle.project")
}
val dataforgeVersion by extra("0.5.0-dev-9")
val dataforgeVersion by extra("0.5.0-dev-10")
val fxVersion by extra("11")
allprojects {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -8,14 +8,11 @@ import ringui.Island
import ringui.SmartTabs
import ringui.Tab
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.visionforge.Vision
import space.kscience.visionforge.getStyle
import space.kscience.visionforge.meta
import space.kscience.visionforge.*
import space.kscience.visionforge.react.flexColumn
import space.kscience.visionforge.react.metaViewer
import space.kscience.visionforge.react.propertyEditor
import space.kscience.visionforge.solid.SolidReference
import space.kscience.visionforge.styles
public fun RBuilder.ringPropertyEditor(
vision: Vision,
@ -31,8 +28,8 @@ public fun RBuilder.ringPropertyEditor(
flexColumn {
Island("Properties") {
propertyEditor(
ownProperties = vision.meta(false,false,false),
allProperties = vision.meta(),
ownProperties = vision.meta,
allProperties = vision.computeProperties(),
updateFlow = vision.propertyChanges,
descriptor = descriptor,
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.asName
import space.kscience.dataforge.names.plus
import space.kscience.dataforge.values.Value
import space.kscience.dataforge.values.asValue
import kotlin.jvm.JvmInline
@ -14,7 +15,7 @@ import kotlin.jvm.JvmInline
@JvmInline
public value class StyleSheet(private val owner: VisionGroup) {
private val styleNode: Meta? get() = owner.getOwnProperty(STYLESHEET_KEY)
private val styleNode: Meta? get() = owner.meta[STYLESHEET_KEY]
public val items: Map<NameToken, Meta>? get() = styleNode?.items
@ -24,7 +25,7 @@ public value class StyleSheet(private val owner: VisionGroup) {
* Define a style without notifying owner
*/
public fun define(key: String, style: Meta?) {
owner.setPropertyNode(STYLESHEET_KEY + key, style)
owner.meta.setMeta(STYLESHEET_KEY + key, style)
}
/**
@ -71,9 +72,9 @@ internal fun Vision.styleChanged(key: String, oldStyle: Meta?, newStyle: Meta?)
* List of names of styles applied to this object. Order matters. Not inherited.
*/
public var Vision.styles: List<String>
get() = getOwnProperty(Vision.STYLE_KEY)?.stringList ?: emptyList()
get() = meta.getMeta(Vision.STYLE_KEY)?.stringList ?: emptyList()
set(value) {
setPropertyValue(Vision.STYLE_KEY, value.map { it.asValue() }.asValue())
meta.setValue(Vision.STYLE_KEY, value.map { it.asValue() }.asValue())
}
/**
@ -86,7 +87,7 @@ public val VisionGroup.styleSheet: StyleSheet get() = StyleSheet(this)
* Add style name to the list of styles to be resolved later. The style with given name does not necessary exist at the moment.
*/
public fun Vision.useStyle(name: String) {
styles = (getOwnProperty(Vision.STYLE_KEY)?.stringList ?: emptyList()) + name
styles = (meta.getMeta(Vision.STYLE_KEY)?.stringList ?: emptyList()) + name
}
@ -94,7 +95,12 @@ public fun Vision.useStyle(name: String) {
* Find a style with given name for given [Vision]. The style is not necessary applied to this [Vision].
*/
public tailrec fun Vision.getStyle(name: String): Meta? =
getOwnProperty(StyleSheet.STYLESHEET_KEY + name) ?: parent?.getStyle(name)
meta.getMeta(StyleSheet.STYLESHEET_KEY + name) ?: parent?.getStyle(name)
/**
* Resolve a property from all styles
*/
public fun Vision.getStyleProperty(name: Name): Value? = styles.firstNotNullOfOrNull { getStyle(it)?.get(name)?.value }
/**
* Resolve an item in all style layers

View File

@ -1,27 +1,27 @@
package space.kscience.visionforge
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.MutableMeta
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.launch
import space.kscience.dataforge.meta.*
import space.kscience.dataforge.meta.descriptors.Described
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.meta.update
import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.misc.Type
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.asName
import space.kscience.dataforge.values.Value
import space.kscience.dataforge.values.asValue
import space.kscience.dataforge.values.boolean
import space.kscience.visionforge.Vision.Companion.TYPE
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
/**
* A root type for display hierarchy
*/
@Type(TYPE)
public interface Vision : Described, CoroutineScope {
public interface Vision : Described, Configurable {
/**
* The parent object of this one. If null, this one is a root.
@ -33,47 +33,23 @@ public interface Vision : Described, CoroutineScope {
*/
public val manager: VisionManager? get() = parent?.manager
override val coroutineContext: CoroutineContext
get() = manager?.context?.coroutineContext ?: EmptyCoroutineContext
/**
* This Vision own properties (ignoring inheritance, styles and defaults
*/
override val meta: ObservableMutableMeta
/**
* Get property.
* Get property value with given layer flags.
* @param inherit toggles parent node property lookup. Null means inference from descriptor. Default is false.
* @param includeStyles toggles inclusion of. Null means inference from descriptor. Default is true.
* @param includeStyles toggles inclusion of properties from styles. default is true
*/
public fun getProperty(
public fun getPropertyValue(
name: Name,
inherit: Boolean = false,
includeStyles: Boolean = true,
includeDefaults: Boolean = true,
): Meta?
): Value?
/**
* Get an intrinsic property of this Vision excluding any inheritance or defaults. In most cases should be the same as
* `getProperty(name, false, false, false`.
*/
public fun getOwnProperty(name: Name): Meta? = getProperty(
name,
inherit = false,
includeStyles = false,
includeDefaults = false
)
/**
* Replace the property node. If [node] is null remove node and its descendants
*/
public fun setPropertyNode(name: Name, node: Meta?, notify: Boolean = true)
/**
* Set a value of specific property node
*/
public fun setPropertyValue(name: Name, value: Value?, notify: Boolean = true)
/**
* Flow of property invalidation events. It does not contain property values after invalidation since it is not clear
* if it should include inherited properties etc.
*/
public val propertyChanges: Flow<Name>
/**
* Notify all listeners that a property has been changed and should be invalidated
@ -83,7 +59,7 @@ public interface Vision : Described, CoroutineScope {
/**
* Update this vision using a dif represented by [VisionChange].
*/
public fun change(change: VisionChange)
public fun update(change: VisionChange)
override val descriptor: MetaDescriptor?
@ -96,58 +72,60 @@ public interface Vision : Described, CoroutineScope {
}
/**
* Subscribe on property updates. The subscription is bound to the given [scope] and canceled when the scope is canceled
* Flow of property invalidation events. It does not contain property values after invalidation since it is not clear
* if it should include inherited properties etc.
*/
public fun Vision.onPropertyChange(scope: CoroutineScope, callback: suspend (Name) -> Unit) {
propertyChanges.onEach(callback).launchIn(scope)
}
@OptIn(ExperimentalCoroutinesApi::class)
@DFExperimental
public val Vision.propertyChanges: Flow<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(
inherit: Boolean = false,
includeStyles: Boolean = true,
includeDefaults: Boolean = true,
): MutableMeta = VisionProperties(this, Name.EMPTY, inherit, includeStyles, includeDefaults)
public fun Vision.configure(target: Name = Name.EMPTY, block: MutableMeta.() -> Unit): Unit {
VisionProperties(this, target).apply(block)
public fun Vision.onPropertyChange(callback: Meta.(Name) -> Unit) {
meta.onChange(null, callback)
}
public fun Vision.configure(meta: Meta) {
configure(Name.EMPTY) {
update(meta)
}
}
public fun Vision.configure(block: MutableMeta.() -> Unit): Unit = configure(Meta(block))
public fun Vision.getOwnProperty(key: String): Meta? = getOwnProperty(Name.parse(key))
/**
* Get [Vision] property using key as a String
*/
public fun Vision.getProperty(
public fun Vision.getPropertyValue(
key: String,
inherit: Boolean = false,
includeStyles: Boolean = true,
includeDefaults: Boolean = true,
): Meta? = getProperty(Name.parse(key), inherit, includeStyles, includeDefaults)
): Value? = getPropertyValue(Name.parse(key), inherit, includeStyles, includeDefaults)
/**
* A convenience method to set property node or value. If Item is null, then node is removed, not a value
*/
public fun Vision.setProperty(name: Name, item: Any?) {
when (item) {
null -> setPropertyNode(name, null)
is Meta -> setPropertyNode(name, item)
is Value -> setPropertyValue(name, item)
null -> meta.remove(name)
is Meta -> meta.setMeta(name, item)
is Value -> meta.setValue(name, item)
else -> meta.setValue(name, Value.of(item))
}
}
public fun Vision.setPropertyNode(key: String, item: Any?) {
setProperty(Name.parse(key), item)
}
/**
* Control visibility of the element
*/
public var Vision.visible: Boolean?
get() = getPropertyValue(Vision.VISIBLE_KEY)?.boolean
set(value) = meta.setValue(Vision.VISIBLE_KEY, value?.asValue())

View File

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

View File

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

View File

@ -1,5 +1,6 @@
package space.kscience.visionforge
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.launch
@ -8,7 +9,6 @@ import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import space.kscience.dataforge.names.*
/**
* Abstract implementation of mutable group of [Vision]
*
@ -48,7 +48,7 @@ public open class VisionGroupBase(
* Propagate children change event upwards
*/
private fun childrenChanged(name: NameToken, before: Vision?, after: Vision?) {
launch {
(manager?.context?: GlobalScope).launch {
_structureChanges.emit(MutableVisionGroup.StructureChange(name, before, after))
}
}
@ -131,15 +131,15 @@ public open class VisionGroupBase(
}
}
override fun change(change: VisionChange) {
override fun update(change: VisionChange) {
change.children?.forEach { (name, change) ->
when {
change.delete -> set(name, null)
change.vision != null -> set(name, change.vision)
else -> get(name)?.change(change)
else -> get(name)?.update(change)
}
}
super.change(change)
super.update(change)
}
}
@ -151,6 +151,6 @@ internal class RootVisionGroup(override val manager: VisionManager) : VisionGrou
/**
* Designate this [VisionGroup] as a root group and assign a [VisionManager] as its parent
*/
public fun Vision.root(manager: VisionManager){
public fun Vision.root(manager: VisionManager) {
parent = RootVisionGroup(manager)
}

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

View File

@ -2,9 +2,7 @@ package space.kscience.visionforge
import space.kscience.dataforge.meta.Laminate
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.boolean
import space.kscience.dataforge.meta.isLeaf
import space.kscience.dataforge.values.asValue
@DslMarker
public annotation class VisionBuilder
@ -17,10 +15,3 @@ public fun List<Meta?>.merge(): 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
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.transformations.MetaConverter
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.values.Value
import space.kscience.dataforge.values.number
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
public fun Vision.propertyNode(
name: Name? = null,
inherit: Boolean = false,
includeStyles: Boolean = true,
includeDefaults: Boolean = true,
): ReadWriteProperty<Any?, Meta?> = object : ReadWriteProperty<Any?, Meta?> {
override fun getValue(thisRef: Any?, property: KProperty<*>): Meta? =
getProperty(name ?: Name.parse(property.name), inherit, includeStyles, includeDefaults)
override fun setValue(thisRef: Any?, property: KProperty<*>, value: Meta?) {
setPropertyNode(name ?: Name.parse(property.name), value)
}
}
public fun <T> Vision.propertyNode(
converter: MetaConverter<T>,
name: Name? = null,
inherit: Boolean = false,
includeStyles: Boolean = true,
includeDefaults: Boolean = true,
): ReadWriteProperty<Any?, T?> = object : ReadWriteProperty<Any?, T?> {
override fun getValue(thisRef: Any?, property: KProperty<*>): T? = getProperty(
name ?: Name.parse(property.name),
inherit,
includeStyles,
includeDefaults
)?.let(converter::metaToObject)
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
setPropertyNode(name ?: Name.parse(property.name), value?.let(converter::objectToMeta))
}
}
//public fun Vision.propertyNode(
// name: Name? = null,
// inherit: Boolean = false,
// includeStyles: Boolean = true,
// includeDefaults: Boolean = true,
//): ReadWriteProperty<Any?, Meta?> = object : ReadWriteProperty<Any?, Meta?> {
// override fun getValue(thisRef: Any?, property: KProperty<*>): Meta? =
// getProperty(name ?: Name.parse(property.name), inherit, includeStyles, includeDefaults)
//
// override fun setValue(thisRef: Any?, property: KProperty<*>, value: Meta?) {
// meta.setMeta(name ?: Name.parse(property.name), value)
// }
//}
//
//public fun <T> Vision.propertyNode(
// converter: MetaConverter<T>,
// name: Name? = null,
// inherit: Boolean = false,
// includeStyles: Boolean = true,
// includeDefaults: Boolean = true,
//): ReadWriteProperty<Any?, T?> = object : ReadWriteProperty<Any?, T?> {
// override fun getValue(thisRef: Any?, property: KProperty<*>): T? = getProperty(
// name ?: Name.parse(property.name),
// inherit,
// includeStyles,
// includeDefaults
// )?.let(converter::metaToObject)
//
// override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
// meta.setMeta(name ?: Name.parse(property.name), value?.let(converter::objectToMeta))
// }
//}
public fun Vision.propertyValue(
name: Name? = null,
@ -48,10 +46,10 @@ public fun Vision.propertyValue(
includeDefaults: Boolean = true,
): ReadWriteProperty<Any?, Value?> = object : ReadWriteProperty<Any?, 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?) {
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) },
getter: (Value?) -> 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),
inherit,
includeStyles,
includeDefaults
)?.value.let(getter)
).let(getter)
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
setPropertyValue(name ?: Name.parse(property.name), value?.let(setter))
meta.setValue(name ?: Name.parse(property.name), value?.let(setter))
}
}

View File

@ -2,7 +2,6 @@ package space.kscience.visionforge
import space.kscience.dataforge.meta.*
import space.kscience.dataforge.meta.descriptors.*
import space.kscience.dataforge.names.asName
import space.kscience.dataforge.values.asValue
private const val INHERITED_DESCRIPTOR_ATTRIBUTE = "inherited"
@ -23,14 +22,6 @@ public var MetaDescriptorBuilder.usesStyles: Boolean
get() = attributes[STYLE_DESCRIPTOR_ATTRIBUTE].boolean ?: true
set(value) = attributes.set(STYLE_DESCRIPTOR_ATTRIBUTE, value)
public val Vision.describedProperties: Meta
get() = Meta {
descriptor?.children?.forEach { (key, descriptor) ->
this.setMeta(key.asName(), getProperty(key, inherit = descriptor.inherited))
}
}
public val MetaDescriptor.widget: Meta
get() = attributes["widget"] ?: Meta.EMPTY

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -6,20 +6,23 @@ import space.kscience.dataforge.values.Value
import space.kscience.dataforge.values.asValue
import space.kscience.dataforge.values.string
import space.kscience.visionforge.Colors
import space.kscience.visionforge.Vision
import space.kscience.visionforge.VisionBuilder
import space.kscience.visionforge.VisionPropertyContainer
import kotlin.jvm.JvmInline
@VisionBuilder
public class ColorAccessor(private val parent: MutableMetaProvider, private val colorKey: Name) {
public class ColorAccessor(private val colorKey: Name, private val parent: () -> MutableMetaProvider) {
public var value: Value?
get() = parent.getMeta(colorKey)?.value
get() = parent().getMeta(colorKey)?.value
set(value) {
parent.setValue(colorKey,value)
parent().setValue(colorKey,value)
}
public var item: Meta?
get() = parent.getMeta(colorKey)
get() = parent().getMeta(colorKey)
set(value) {
parent.setMeta(colorKey,value)
parent().setMeta(colorKey,value)
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,12 +1,10 @@
package space.kscience.visionforge.solid
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.ObservableMutableMeta
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.meta.get
import space.kscience.dataforge.names.*
import space.kscience.dataforge.values.Value
import space.kscience.visionforge.*
@ -17,6 +15,18 @@ public interface SolidReference : VisionGroup {
* The prototype for this reference. Always returns a "real" prototype, not a reference
*/
public val prototype: Solid
override fun getPropertyValue(name: Name, inherit: Boolean, includeStyles: Boolean, includeDefaults: Boolean): Value? {
meta[name]?.value?.let { return it }
if (includeStyles) {
getStyleProperty(name)?.let { return it }
}
prototype.getPropertyValue(name, inherit, includeStyles, includeDefaults)?.let { return it }
if (inherit) {
parent?.getPropertyValue(name, inherit, includeStyles, includeDefaults)?.let { return it }
}
return null
}
}
@ -31,27 +41,6 @@ public val Vision.unref: Solid
else -> error("This Vision is neither Solid nor SolidReference")
}
private fun SolidReference.getRefProperty(
name: Name,
inherit: Boolean,
includeStyles: Boolean,
includeDefaults: Boolean,
): Meta? = if (!inherit && !includeStyles && !includeDefaults) {
getOwnProperty(name)
} else {
buildList {
add(getOwnProperty(name))
if (includeStyles) {
addAll(getStyleItems(name))
}
add(prototype.getProperty(name, inherit, includeStyles, includeDefaults))
if (inherit) {
add(parent?.getProperty(name, inherit))
}
}.merge()
}
private fun childToken(childName: Name): NameToken =
NameToken(SolidReferenceGroup.REFERENCE_CHILD_PROPERTY_PREFIX, childName.toString())
@ -83,12 +72,8 @@ public class SolidReferenceGroup(
ReferenceChild(this, it.key.asName())
} ?: emptyMap()
override fun getProperty(
name: Name,
inherit: Boolean,
includeStyles: Boolean,
includeDefaults: Boolean,
): Meta? = getRefProperty(name, inherit, includeStyles, includeDefaults)
override fun getPropertyValue(name: Name, inherit: Boolean, includeStyles: Boolean, includeDefaults: Boolean): Value? =
super<SolidReference>.getPropertyValue(name, inherit, includeStyles, includeDefaults)
override val descriptor: MetaDescriptor get() = prototype.descriptor
@ -111,6 +96,10 @@ public class SolidReferenceGroup(
}
}
override val meta: ObservableMutableMeta by lazy {
owner.meta.getOrCreate(childToken(refName).asName())
}
override val children: Map<NameToken, Vision>
get() = (prototype as? VisionGroup)?.children
?.filter { it.key != SolidGroup.PROTOTYPES_TOKEN }
@ -118,24 +107,6 @@ public class SolidReferenceGroup(
ReferenceChild(owner, refName + key.asName())
} ?: emptyMap()
override fun getOwnProperty(name: Name): Meta? =
owner.getOwnProperty(childPropertyName(refName, name))
override fun setPropertyNode(name: Name, node: Meta?, notify: Boolean) {
owner.setPropertyNode(childPropertyName(refName, name), node, notify)
}
override fun setPropertyValue(name: Name, value: Value?, notify: Boolean) {
owner.setPropertyValue(childPropertyName(refName, name), value, notify)
}
override fun getProperty(
name: Name,
inherit: Boolean,
includeStyles: Boolean,
includeDefaults: Boolean,
): Meta? = getRefProperty(name, inherit, includeStyles, includeDefaults)
override var parent: VisionGroup?
get() {
val parentName = refName.cutLast()
@ -145,21 +116,11 @@ public class SolidReferenceGroup(
error("Setting a parent for a reference child is not possible")
}
@DFExperimental
override val propertyChanges: Flow<Name>
get() = owner.propertyChanges.mapNotNull { name ->
if (name.startsWith(childToken(refName))) {
name.cutFirst()
} else {
null
}
}
override fun invalidateProperty(propertyName: Name) {
owner.invalidateProperty(childPropertyName(refName, propertyName))
}
override fun change(change: VisionChange) {
override fun update(change: VisionChange) {
change.properties?.let {
updateProperties(Name.EMPTY, it)
}

View File

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

View File

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

View File

@ -2,11 +2,11 @@ package space.kscience.visionforge.solid.specifications
import space.kscience.dataforge.meta.*
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.meta.descriptors.scheme
import space.kscience.dataforge.meta.descriptors.value
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.values.ValueType
import space.kscience.visionforge.hide
import space.kscience.visionforge.scheme
import space.kscience.visionforge.value
import space.kscience.visionforge.widgetType
public class Clipping : Scheme() {
@ -80,7 +80,19 @@ public class Canvas3DOptions : Scheme() {
override val descriptor: MetaDescriptor by lazy {
MetaDescriptor {
scheme(Canvas3DOptions::axes, Axes)
scheme(Canvas3DOptions::light, Light)
value(Canvas3DOptions::layers) {
multiple = true
default(listOf(0))
widgetType = "multiSelect"
allowedValues(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
}
scheme(Canvas3DOptions::clipping, Clipping)
scheme(Canvas3DOptions::light, Light){
hide()
}
scheme(Canvas3DOptions::camera, Camera) {
hide()
@ -93,15 +105,6 @@ public class Canvas3DOptions : Scheme() {
scheme(Canvas3DOptions::size, CanvasSize) {
hide()
}
value(Canvas3DOptions::layers) {
type(ValueType.NUMBER)
multiple = true
default(listOf(0))
widgetType = "multiSelect"
allowedValues(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
}
scheme(Canvas3DOptions::clipping, Clipping)
}
}
}

View File

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

View File

@ -1,13 +1,24 @@
package space.kscience.visionforge.solid
import space.kscience.dataforge.meta.int
import space.kscience.dataforge.meta.*
import space.kscience.dataforge.names.asName
import space.kscience.dataforge.values.int
import space.kscience.visionforge.*
import kotlin.test.Test
import kotlin.test.assertEquals
@Suppress("UNUSED_VARIABLE")
class PropertyTest {
@Test
fun testColorUpdate(){
val box = Box(10.0f, 10.0f,10.0f)
box.material {
//meta["color"] = "pink"
color("pink")
}
assertEquals("pink", box.meta["material.color"]?.string)
}
@Test
fun testInheritedProperty() {
var box: Box? = null
@ -17,7 +28,7 @@ class PropertyTest {
box = box(100, 100, 100)
}
}
assertEquals(22, box?.getProperty("test", inherit = true).int)
assertEquals(22, box?.getPropertyValue("test", inherit = true)?.int)
}
@Test
@ -35,11 +46,11 @@ class PropertyTest {
}
}
}
assertEquals(22, box?.getProperty("test").int)
assertEquals(22, box?.getPropertyValue("test")?.int)
}
@Test
fun testColor() {
fun testStyleColor() {
var box: Box? = null
val group = SolidGroup().apply {
styleSheet {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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