A lot of changes
This commit is contained in:
parent
8d21d3cd74
commit
7b78052f61
@ -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 {
|
||||
|
@ -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")
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -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())
|
||||
}
|
||||
|
@ -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"))
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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]
|
@ -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
|
||||
|
@ -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())
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
@ -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)
|
||||
}
|
@ -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
|
||||
}
|
@ -2,9 +2,7 @@ package space.kscience.visionforge
|
||||
|
||||
import space.kscience.dataforge.meta.Laminate
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.boolean
|
||||
import space.kscience.dataforge.meta.isLeaf
|
||||
import space.kscience.dataforge.values.asValue
|
||||
|
||||
@DslMarker
|
||||
public annotation class VisionBuilder
|
||||
@ -16,11 +14,4 @@ public fun List<Meta?>.merge(): Meta? {
|
||||
first.isLeaf -> first //fast search for first entry if it is value
|
||||
else -> Laminate(filterNotNull()) //merge nodes if first encountered node is meta
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Control visibility of the element
|
||||
*/
|
||||
public var Vision.visible: Boolean?
|
||||
get() = getProperty(Vision.VISIBLE_KEY).boolean
|
||||
set(value) = setPropertyValue(Vision.VISIBLE_KEY, value?.asValue())
|
||||
}
|
@ -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)
|
||||
}
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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() }
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -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")
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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())
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
|
@ -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),
|
||||
|
@ -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" }
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -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) ||
|
||||
|
@ -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()
|
||||
|
@ -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")
|
||||
|
Loading…
Reference in New Issue
Block a user