[WIP] Change property and structure subscription to flows.

This commit is contained in:
Alexander Nozik 2020-12-17 21:25:25 +03:00
parent 6a6d9659ca
commit 97cdfd3719
31 changed files with 250 additions and 207 deletions

View File

@ -10,9 +10,9 @@ import hep.dataforge.vision.bootstrap.canvasControls
import hep.dataforge.vision.bootstrap.card import hep.dataforge.vision.bootstrap.card
import hep.dataforge.vision.bootstrap.gridRow import hep.dataforge.vision.bootstrap.gridRow
import hep.dataforge.vision.react.ThreeCanvasComponent import hep.dataforge.vision.react.ThreeCanvasComponent
import hep.dataforge.vision.react.configEditor
import hep.dataforge.vision.react.flexColumn import hep.dataforge.vision.react.flexColumn
import hep.dataforge.vision.react.objectTree import hep.dataforge.vision.react.objectTree
import hep.dataforge.vision.react.propertyEditor
import hep.dataforge.vision.solid.specifications.Camera import hep.dataforge.vision.solid.specifications.Camera
import hep.dataforge.vision.solid.specifications.Canvas3DOptions import hep.dataforge.vision.solid.specifications.Canvas3DOptions
import hep.dataforge.vision.solid.three.ThreeCanvas import hep.dataforge.vision.solid.three.ThreeCanvas
@ -193,7 +193,7 @@ val MMApp = functionalComponent<MMAppProps>("Muon monitor") { props ->
else -> root[selected] else -> root[selected]
} }
if (selectedObject != null) { if (selectedObject != null) {
configEditor( propertyEditor(
selectedObject.config, selectedObject.config,
selectedObject.descriptor, selectedObject.descriptor,
default = selectedObject.allProperties, default = selectedObject.allProperties,

View File

@ -5,7 +5,6 @@ import hep.dataforge.names.plus
import hep.dataforge.names.startsWith import hep.dataforge.names.startsWith
import hep.dataforge.values.asValue import hep.dataforge.values.asValue
import hep.dataforge.vision.getProperty import hep.dataforge.vision.getProperty
import hep.dataforge.vision.properties
import hep.dataforge.vision.set import hep.dataforge.vision.set
import hep.dataforge.vision.setProperty import hep.dataforge.vision.setProperty
import hep.dataforge.vision.solid.* import hep.dataforge.vision.solid.*
@ -34,8 +33,8 @@ internal class VariableBox(xSize: Number, ySize: Number, zSize: Number) : ThreeV
scaleX = xSize scaleX = xSize
scaleY = ySize scaleY = ySize
scaleZ = zSize scaleZ = zSize
properties[MeshThreeFactory.EDGES_ENABLED_KEY] = false getProperty(MeshThreeFactory.EDGES_ENABLED_KEY, false)
properties[MeshThreeFactory.WIREFRAME_ENABLED_KEY] = false getProperty(MeshThreeFactory.WIREFRAME_ENABLED_KEY, false)
} }
override fun render(three: ThreePlugin): Object3D { override fun render(three: ThreePlugin): Object3D {
@ -63,7 +62,7 @@ internal class VariableBox(xSize: Number, ySize: Number, zSize: Number) : ThreeV
mesh.scale.set(xSize, ySize, zSize) mesh.scale.set(xSize, ySize, zSize)
//add listener to object properties //add listener to object properties
propertyInvalidated.onEach { name -> propertyNameFlow.onEach { name ->
when { when {
name.startsWith(GEOMETRY_KEY) -> { name.startsWith(GEOMETRY_KEY) -> {
val newXSize = getProperty(X_SIZE_KEY, false).number?.toDouble() ?: 1.0 val newXSize = getProperty(X_SIZE_KEY, false).number?.toDouble() ?: 1.0

View File

@ -1,6 +1,6 @@
pluginManagement { pluginManagement {
val kotlinVersion = "1.4.20" val kotlinVersion = "1.4.21"
val toolsVersion = "0.7.0" val toolsVersion = "0.7.1"
repositories { repositories {
mavenLocal() mavenLocal()

View File

@ -3,29 +3,30 @@ package hep.dataforge.vision.bootstrap
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.meta.descriptors.NodeDescriptor import hep.dataforge.meta.descriptors.NodeDescriptor
import hep.dataforge.vision.Vision import hep.dataforge.vision.Vision
import hep.dataforge.vision.allProperties
import hep.dataforge.vision.getStyle import hep.dataforge.vision.getStyle
import hep.dataforge.vision.properties
import hep.dataforge.vision.react.configEditor import hep.dataforge.vision.react.configEditor
import hep.dataforge.vision.react.metaViewer import hep.dataforge.vision.react.metaViewer
import hep.dataforge.vision.styles
import org.w3c.dom.Element import org.w3c.dom.Element
import react.RBuilder import react.RBuilder
import react.dom.render import react.dom.render
public fun RBuilder.visionPropertyEditor( public fun RBuilder.visionPropertyEditor(
item: Vision, vision: Vision,
descriptor: NodeDescriptor? = item.descriptor, descriptor: NodeDescriptor? = vision.descriptor,
default: Meta? = null, default: Meta? = null,
key: Any? = null key: Any? = null,
) { ) {
card("Properties") { card("Properties") {
configEditor(item.properties, descriptor, default, key) configEditor(vision.allProperties(), descriptor, default, key)
} }
val styles = item.styles val styles = vision.styles
if(styles.isNotEmpty()) { if (styles.isNotEmpty()) {
card("Styles") { card("Styles") {
accordion("styles") { accordion("styles") {
styles.forEach { styleName -> styles.forEach { styleName ->
val style = item.getStyle(styleName) val style = vision.getStyle(styleName)
if (style != null) { if (style != null) {
entry(styleName) { entry(styleName) {
metaViewer(style) metaViewer(style)
@ -40,7 +41,7 @@ public fun RBuilder.visionPropertyEditor(
public fun Element.visionPropertyEditor( public fun Element.visionPropertyEditor(
item: Vision, item: Vision,
descriptor: NodeDescriptor? = item.descriptor, descriptor: NodeDescriptor? = item.descriptor,
default: Meta? = null default: Meta? = null,
): Unit = render(this) { ): Unit = render(this) {
visionPropertyEditor(item, descriptor, default) visionPropertyEditor(item, descriptor, default)
} }

View File

@ -7,6 +7,14 @@ import hep.dataforge.names.NameToken
import hep.dataforge.names.lastOrNull import hep.dataforge.names.lastOrNull
import hep.dataforge.names.plus import hep.dataforge.names.plus
import hep.dataforge.values.Value import hep.dataforge.values.Value
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import kotlinx.html.js.onClickFunction import kotlinx.html.js.onClickFunction
import org.w3c.dom.Element import org.w3c.dom.Element
import org.w3c.dom.events.Event import org.w3c.dom.events.Event
@ -14,55 +22,59 @@ import react.*
import react.dom.render import react.dom.render
import styled.* import styled.*
public external interface ConfigEditorItemProps : RProps { public external interface PropertyEditorItemProps : RProps {
/** /**
* Root config object - always non null * Root config object - always non null
*/ */
public var root: MutableItemProvider public var provider: MutableItemProvider
/** /**
* Full path to the displayed node in [root]. Could be empty * Full path to the displayed node in [provider]. Could be empty
*/ */
public var name: Name public var name: Name
/**
* Root default
*/
public var default: Meta?
/** /**
* Root descriptor * Root descriptor
*/ */
public var descriptor: NodeDescriptor? public var descriptor: NodeDescriptor?
public var scope: CoroutineScope?
/**
*
*/
public var updateFlow: Flow<Name>?
} }
private val ConfigEditorItem: FunctionalComponent<ConfigEditorItemProps> = private val PropertyEditorItem: FunctionalComponent<PropertyEditorItemProps> =
functionalComponent("ConfigEditorItem") { props -> functionalComponent("ConfigEditorItem") { props ->
configEditorItem(props) propertyEditorItem(props)
} }
private fun RBuilder.configEditorItem(props: ConfigEditorItemProps) { private fun RBuilder.propertyEditorItem(props: PropertyEditorItemProps) {
var expanded: Boolean by useState { true } var expanded: Boolean by useState { true }
var item: MetaItem<*>? by useState { props.root.getItem(props.name) } var item: MetaItem<*>? by useState { props.provider.getItem(props.name) }
val descriptorItem: ItemDescriptor? = props.descriptor?.get(props.name) val descriptorItem: ItemDescriptor? = props.descriptor?.get(props.name)
val defaultItem = props.default?.get(props.name) var actualItem: MetaItem<Meta>? by useState { item ?: descriptorItem?.defaultItem() }
var actualItem: MetaItem<Meta>? by useState { item ?: defaultItem ?: descriptorItem?.defaultItem() }
val token = props.name.lastOrNull()?.toString() ?: "Properties" val token = props.name.lastOrNull()?.toString() ?: "Properties"
fun update() { fun update() {
item = props.root.getItem(props.name) item = props.provider.getItem(props.name)
actualItem = item ?: defaultItem ?: descriptorItem?.defaultItem() actualItem = item ?: descriptorItem?.defaultItem()
} }
useEffectWithCleanup(listOf(props.root)) { if (props.updateFlow != null) {
props.root.onChange(this) { name, _, _ -> useEffectWithCleanup(listOf(props.provider, props.updateFlow)) {
if (name == props.name) { val updateJob = props.updateFlow!!.onEach { updatedName ->
update() if (updatedName == props.name) {
} update()
}
}.launchIn(props.scope ?: GlobalScope)
return@useEffectWithCleanup { updateJob.cancel() }
} }
return@useEffectWithCleanup { props.root.removeListener(this) }
} }
val expanderClick: (Event) -> Unit = { val expanderClick: (Event) -> Unit = {
@ -71,15 +83,15 @@ private fun RBuilder.configEditorItem(props: ConfigEditorItemProps) {
val valueChanged: (Value?) -> Unit = { val valueChanged: (Value?) -> Unit = {
if (it == null) { if (it == null) {
props.root.remove(props.name) props.provider.remove(props.name)
} else { } else {
props.root[props.name] = it props.provider[props.name] = it
} }
update() update()
} }
val removeClick: (Event) -> Unit = { val removeClick: (Event) -> Unit = {
props.root.remove(props.name) props.provider.remove(props.name)
update() update()
} }
@ -121,7 +133,6 @@ private fun RBuilder.configEditorItem(props: ConfigEditorItemProps) {
add(NameToken(it)) add(NameToken(it))
} }
item?.node?.items?.keys?.let { addAll(it) } item?.node?.items?.keys?.let { addAll(it) }
defaultItem?.node?.items?.keys?.let { addAll(it) }
} }
keys.filter { !it.body.startsWith("@") }.forEach { token -> keys.filter { !it.body.startsWith("@") }.forEach { token ->
@ -129,12 +140,11 @@ private fun RBuilder.configEditorItem(props: ConfigEditorItemProps) {
css { css {
+TreeStyles.treeItem +TreeStyles.treeItem
} }
child(ConfigEditorItem) { child(PropertyEditorItem) {
attrs { attrs {
this.key = props.name.toString() this.key = props.name.toString()
this.root = props.root this.provider = props.provider
this.name = props.name + token this.name = props.name + token
this.default = props.default
this.descriptor = props.descriptor this.descriptor = props.descriptor
} }
} }
@ -190,63 +200,79 @@ private fun RBuilder.configEditorItem(props: ConfigEditorItemProps) {
} }
} }
public external interface ConfigEditorProps : RProps { public external interface PropertyEditorProps : RProps {
public var id: Name public var provider: MutableItemProvider
public var root: MutableItemProvider public var updateFlow: Flow<Name>?
public var default: Meta?
public var descriptor: NodeDescriptor? public var descriptor: NodeDescriptor?
public var scope: CoroutineScope?
} }
@JsExport @JsExport
public val ConfigEditor: FunctionalComponent<ConfigEditorProps> = functionalComponent("ConfigEditor") { props -> public val PropertyEditor: FunctionalComponent<PropertyEditorProps> = functionalComponent("ConfigEditor") { props ->
child(ConfigEditorItem) { child(PropertyEditorItem) {
attrs { attrs {
this.key = "" this.key = ""
this.root = props.root this.provider = props.provider
this.name = Name.EMPTY this.name = Name.EMPTY
this.default = props.default
this.descriptor = props.descriptor this.descriptor = props.descriptor
this.scope = props.scope
} }
} }
} }
public fun RBuilder.propertyEditor(
provider: MutableItemProvider,
updateFlow: Flow<Name>? = null,
descriptor: NodeDescriptor? = null,
key: Any? = null,
scope: CoroutineScope? = null,
) {
child(PropertyEditor) {
attrs {
this.key = key?.toString() ?: ""
this.provider = provider
this.updateFlow = updateFlow
this.descriptor = descriptor
this.scope = scope
}
}
}
private fun Config.flowUpdates(): Flow<Name> = callbackFlow {
onChange(this) { name, _, _ ->
launch {
send(name)
}
}
awaitClose {
removeListener(this)
}
}
public fun MutableItemProvider.withDefault(default: ItemProvider): MutableItemProvider = object : MutableItemProvider {
override fun getItem(name: Name): MetaItem<*>? = getItem(name) ?: default.getItem(name)
override fun setItem(name: Name, item: MetaItem<*>?) = this@withDefault.setItem(name, item)
}
public fun RBuilder.configEditor(
config: Config,
descriptor: NodeDescriptor? = null,
default: Meta? = null,
key: Any? = null,
scope: CoroutineScope? = null,
) = propertyEditor(config.withDefault(default ?: ItemProvider.EMPTY), config.flowUpdates(), descriptor, key, scope)
public fun Element.configEditor( public fun Element.configEditor(
config: Config, config: Config,
descriptor: NodeDescriptor? = null, descriptor: NodeDescriptor? = null,
default: Meta? = null, default: Meta? = null,
key: Any? = null, key: Any? = null,
scope: CoroutineScope? = null,
) { ) {
render(this) { render(this) {
child(ConfigEditor) { configEditor(config,descriptor,default, key, scope)
attrs {
this.key = key?.toString() ?: ""
this.root = config
this.descriptor = descriptor
this.default = default
}
}
} }
} }
public fun RBuilder.configEditor(
config: MutableItemProvider,
descriptor: NodeDescriptor? = null,
default: Meta? = null,
key: Any? = null,
) {
child(ConfigEditor) {
attrs {
this.key = key?.toString() ?: ""
this.root = config
this.descriptor = descriptor
this.default = default
}
}
}
//
//public fun RBuilder.configEditor(
// obj: Configurable,
// descriptor: NodeDescriptor?,
// default: Meta? = null,
// key: Any? = null
//): Unit = configEditor(obj.config, descriptor, default, key)

View File

@ -64,6 +64,15 @@ 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()
set(value) {
setProperty(Vision.STYLE_KEY, value)
}
/** /**
* A stylesheet for this group and its descendants. Stylesheet is not applied directly, * A stylesheet for this group and its descendants. Stylesheet is not applied directly,
* but instead is just a repository for named configurations. * but instead is just a repository for named configurations.

View File

@ -1,15 +1,15 @@
package hep.dataforge.vision package hep.dataforge.vision
import hep.dataforge.meta.* import hep.dataforge.meta.MetaItem
import hep.dataforge.meta.MutableItemProvider
import hep.dataforge.meta.descriptors.Described import hep.dataforge.meta.descriptors.Described
import hep.dataforge.meta.descriptors.NodeDescriptor import hep.dataforge.meta.descriptors.NodeDescriptor
import hep.dataforge.meta.descriptors.get
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.asName import hep.dataforge.names.asName
import hep.dataforge.names.toName import hep.dataforge.names.toName
import hep.dataforge.provider.Type import hep.dataforge.provider.Type
import hep.dataforge.values.asValue
import hep.dataforge.vision.Vision.Companion.TYPE import hep.dataforge.vision.Vision.Companion.TYPE
import hep.dataforge.vision.Vision.Companion.VISIBLE_KEY
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.serialization.Transient import kotlinx.serialization.Transient
@ -26,7 +26,8 @@ public interface Vision : Described {
public var parent: VisionGroup? public var parent: VisionGroup?
/** /**
* A fast accessor method to get own property (no inheritance or styles * A fast accessor method to get own property (no inheritance or styles).
* Should be equivalent to `getProperty(name,false,false,false)`.
*/ */
public fun getOwnProperty(name: Name): MetaItem<*>? public fun getOwnProperty(name: Name): MetaItem<*>?
@ -46,13 +47,13 @@ public interface Vision : Described {
/** /**
* Set the property value * Set the property value
*/ */
public fun setProperty(name: Name, item: MetaItem<*>?) public fun setProperty(name: Name, item: MetaItem<*>?, notify: Boolean = true)
/** /**
* Flow of property invalidation events. It does not contain property values after invalidation since it is not clear * Flow of property invalidation events. It does not contain property values after invalidation since it is not clear
* if it should include inherited properties etc. * if it should include inherited properties etc.
*/ */
public val propertyInvalidated: Flow<Name> public val propertyNameFlow: Flow<Name>
/** /**
@ -60,20 +61,6 @@ public interface Vision : Described {
*/ */
public fun notifyPropertyChanged(propertyName: Name): Unit public fun notifyPropertyChanged(propertyName: Name): Unit
/**
* List of names of styles applied to this object. Order matters. Not inherited.
*/
public var styles: List<String>
get() = getProperty(
STYLE_KEY,
inherit = false,
includeStyles = false,
includeDefaults = true
)?.stringList ?: emptyList()
set(value) {
setProperty(STYLE_KEY, value)
}
/** /**
* Update this vision using external meta. Children are not updated. * Update this vision using external meta. Children are not updated.
*/ */
@ -90,19 +77,27 @@ public interface Vision : Described {
} }
/** /**
* Convenient accessor for all properties of a vision. Provided properties include styles and defaults, but do not inherit. * Convenient accessor for all properties of a vision.
* @param inherit - inherit property value from the parent by default. If null, inheritance is inferred from descriptor
*/ */
public val Vision.properties: MutableItemProvider public fun Vision.allProperties(
get() = object : MutableItemProvider { inherit: Boolean? = null,
override fun getItem(name: Name): MetaItem<*>? = getProperty(name, includeStyles: Boolean = true,
inherit = false, includeDefaults: Boolean = true,
includeStyles = true, ): MutableItemProvider = object : MutableItemProvider {
includeDefaults = true override fun getItem(name: Name): MetaItem<*>? {
val actualInherit = inherit ?: descriptor?.get(name)?.inherited ?: false
return getProperty(
name,
inherit = actualInherit,
includeStyles = includeStyles,
includeDefaults = includeDefaults
) )
override fun setItem(name: Name, item: MetaItem<*>?): Unit = setProperty(name, item)
} }
override fun setItem(name: Name, item: MetaItem<*>?): Unit = setProperty(name, item)
}
/** /**
* Get [Vision] property using key as a String * Get [Vision] property using key as a String
*/ */
@ -116,31 +111,13 @@ public fun Vision.getProperty(
/** /**
* A convenience method to pair [getProperty] * A convenience method to pair [getProperty]
*/ */
public fun Vision.setProperty(key: Name, value: Any?) { public fun Vision.setProperty(key: Name, item: Any?) {
properties[key] = value setProperty(key, MetaItem.of(item))
} }
/** /**
* A convenience method to pair [getProperty] * A convenience method to pair [getProperty]
*/ */
public fun Vision.setProperty(key: String, value: Any?) { public fun Vision.setProperty(key: String, item: Any?) {
properties[key] = value setProperty(key.toName(), MetaItem.of(item))
} }
/**
* Control visibility of the element
*/
public var Vision.visible: Boolean?
get() = getProperty(VISIBLE_KEY).boolean
set(value) = setProperty(VISIBLE_KEY, value?.asValue())
public fun Vision.props(inherit: Boolean = true): MutableItemProvider = object : MutableItemProvider {
override fun getItem(name: Name): MetaItem<*>? {
return getProperty(name, inherit)
}
override fun setItem(name: Name, item: MetaItem<*>?) {
setProperty(name, item)
}
}

View File

@ -1,9 +1,12 @@
package hep.dataforge.vision package hep.dataforge.vision
import hep.dataforge.meta.* import hep.dataforge.meta.Config
import hep.dataforge.meta.MetaItem
import hep.dataforge.meta.MutableMeta
import hep.dataforge.meta.descriptors.NodeDescriptor import hep.dataforge.meta.descriptors.NodeDescriptor
import hep.dataforge.meta.descriptors.defaultItem import hep.dataforge.meta.descriptors.defaultItem
import hep.dataforge.meta.descriptors.get import hep.dataforge.meta.descriptors.get
import hep.dataforge.meta.update
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.asName import hep.dataforge.names.asName
import hep.dataforge.values.ValueType import hep.dataforge.values.ValueType
@ -30,34 +33,28 @@ public open class VisionBase : Vision {
/** /**
* Object own properties excluding styles and inheritance * Object own properties excluding styles and inheritance
*/ */
@SerialName("properties") public var properties: Config? = null
protected var innerProperties: Config? = null
private set private set
/**
* All own properties as a read-only Meta
*/
public val ownProperties: Meta get() = innerProperties ?: Meta.EMPTY
@Synchronized @Synchronized
private fun getOrCreateConfig(): Config { private fun getOrCreateConfig(): Config {
if (innerProperties == null) { if (properties == null) {
val newProperties = Config() val newProperties = Config()
innerProperties = newProperties properties = newProperties
newProperties.onChange(this) { name, oldItem, newItem -> newProperties.onChange(this) { name, oldItem, newItem ->
if (oldItem != newItem) { if (oldItem != newItem) {
notifyPropertyChanged(name) notifyPropertyChanged(name)
} }
} }
} }
return innerProperties!! return properties!!
} }
/** /**
* A fast accessor method to get own property (no inheritance or styles * A fast accessor method to get own property (no inheritance or styles
*/ */
override fun getOwnProperty(name: Name): MetaItem<*>? { override fun getOwnProperty(name: Name): MetaItem<*>? {
return innerProperties?.getItem(name) return properties?.getItem(name)
} }
override fun getProperty( override fun getProperty(
@ -77,9 +74,11 @@ public open class VisionBase : Vision {
}.merge() }.merge()
@Synchronized @Synchronized
override fun setProperty(name: Name, item: MetaItem<*>?) { override fun setProperty(name: Name, item: MetaItem<*>?, notify: Boolean) {
getOrCreateConfig().setItem(name, item) getOrCreateConfig().setItem(name, item)
notifyPropertyChanged(name) if(notify) {
notifyPropertyChanged(name)
}
} }
override val descriptor: NodeDescriptor? get() = null override val descriptor: NodeDescriptor? get() = null
@ -96,11 +95,11 @@ public open class VisionBase : Vision {
@Transient @Transient
private val _propertyInvalidationFlow: MutableSharedFlow<Name> = MutableSharedFlow() private val _propertyInvalidationFlow: MutableSharedFlow<Name> = MutableSharedFlow()
override val propertyInvalidated: SharedFlow<Name> get() = _propertyInvalidationFlow override val propertyNameFlow: SharedFlow<Name> get() = _propertyInvalidationFlow
override fun notifyPropertyChanged(propertyName: Name) { override fun notifyPropertyChanged(propertyName: Name) {
if (propertyName == STYLE_KEY) { if (propertyName == STYLE_KEY) {
updateStyles(properties.getItem(STYLE_KEY)?.stringList ?: emptyList()) updateStyles(styles)
} }
_propertyInvalidationFlow.tryEmit(propertyName) _propertyInvalidationFlow.tryEmit(propertyName)

View File

@ -64,7 +64,7 @@ private fun Vision.isolate(manager: VisionManager): Vision {
public class VisionChange( public class VisionChange(
public val reset: Boolean = false, public val reset: Boolean = false,
public val vision: Vision? = null, public val vision: Vision? = null,
public val properties: Meta? = null, @Serializable(MetaSerializer::class) public val properties: Meta? = null,
public val children: Map<Name, VisionChange>? = null, public val children: Map<Name, VisionChange>? = null,
) { ) {
@ -81,7 +81,7 @@ private fun CoroutineScope.collectChange(
) { ) {
//Collect properties change //Collect properties change
source.propertyInvalidated.onEach { propertyName -> source.propertyNameFlow.onEach { propertyName ->
val newItem = source.getProperty(propertyName, inherit = false, includeStyles = false, includeDefaults = false) val newItem = source.getProperty(propertyName, inherit = false, includeStyles = false, includeDefaults = false)
collector().propertyChanged(name, propertyName, newItem) collector().propertyChanged(name, propertyName, newItem)
}.launchIn(this) }.launchIn(this)

View File

@ -31,16 +31,15 @@ public inline fun <reified E : Enum<E>> NodeDescriptor.enum(key: Name, default:
} }
@DFExperimental @DFExperimental
public val Vision.ownProperties: Meta? public val Vision.properties: Config?
get() = (this as? VisionBase)?.ownProperties get() = (this as? VisionBase)?.properties
@DFExperimental /**
public val Vision.describedProperties: Meta * Control visibility of the element
get() = Meta { */
descriptor?.items?.forEach { (key, _) -> public var Vision.visible: Boolean?
key put getProperty(key) get() = getProperty(Vision.VISIBLE_KEY).boolean
} set(value) = setProperty(Vision.VISIBLE_KEY, value?.asValue())
}
public fun Vision.configure(meta: Meta?): Unit = update(VisionChange(properties = meta)) public fun Vision.configure(meta: Meta?): Unit = update(VisionChange(properties = meta))

View File

@ -0,0 +1,25 @@
package hep.dataforge.vision
import hep.dataforge.meta.Meta
import hep.dataforge.meta.boolean
import hep.dataforge.meta.descriptors.ItemDescriptor
import hep.dataforge.meta.descriptors.attributes
import hep.dataforge.meta.get
import hep.dataforge.meta.set
private const val INHERITED_DESCRIPTOR_ATTRIBUTE = "inherited"
public var ItemDescriptor.inherited: Boolean
get() = attributes[INHERITED_DESCRIPTOR_ATTRIBUTE].boolean ?: false
set(value) = attributes {
set(INHERITED_DESCRIPTOR_ATTRIBUTE, value)
}
public val Vision.describedProperties: Meta
get() = Meta {
descriptor?.items?.forEach { (key, descriptor) ->
key put getProperty(key, inherit = descriptor.inherited)
}
}

View File

@ -19,8 +19,8 @@ class VisualObjectEditorFragment(val selector: (Vision) -> Meta) : Fragment() {
constructor( constructor(
item: Vision?, item: Vision?,
descriptor: NodeDescriptor?, descriptor: NodeDescriptor?,
selector: (Vision) -> MutableItemProvider = { it.properties } selector: (Vision) -> MutableItemProvider = { it.allProperties() },
) : this({it.describedProperties}) { ) : this({ it.describedProperties }) {
this.item = item this.item = item
this.descriptorProperty.set(descriptor) this.descriptorProperty.set(descriptor)
} }

View File

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

View File

@ -18,7 +18,7 @@ class VisualObjectFXBinding(val fx: FX3DPlugin, val obj: Vision) {
private val bindings = HashMap<Name, ObjectBinding<MetaItem<*>?>>() private val bindings = HashMap<Name, ObjectBinding<MetaItem<*>?>>()
init { init {
obj.propertyInvalidated.onEach { name -> obj.propertyNameFlow.onEach { name ->
bindings.filter { it.key.startsWith(name) }.forEach { entry -> bindings.filter { it.key.startsWith(name) }.forEach { entry ->
Platform.runLater { Platform.runLater {
entry.value.invalidate() entry.value.invalidate()

View File

@ -38,7 +38,6 @@ public class GDMLTransformerSettings {
public var solidAction: (GDMLSolid) -> Action = { Action.PROTOTYPE } public var solidAction: (GDMLSolid) -> Action = { Action.PROTOTYPE }
public var volumeAction: (GDMLGroup) -> Action = { Action.PROTOTYPE } public var volumeAction: (GDMLGroup) -> Action = { Action.PROTOTYPE }
} }
private class GDMLTransformer(val settings: GDMLTransformerSettings) { private class GDMLTransformer(val settings: GDMLTransformerSettings) {
@ -324,7 +323,7 @@ private class GDMLTransformer(val settings: GDMLTransformerSettings) {
} }
} }
fun finalize(final: SolidGroup): SolidGroup { private fun finalize(final: SolidGroup): SolidGroup {
//final.prototypes = proto //final.prototypes = proto
final.useStyle("GDML") { final.useStyle("GDML") {
Solid.ROTATION_ORDER_KEY put RotationOrder.ZXY Solid.ROTATION_ORDER_KEY put RotationOrder.ZXY

View File

@ -3,7 +3,6 @@ package hep.dataforge.vision.gdml
import hep.dataforge.meta.DFExperimental import hep.dataforge.meta.DFExperimental
import hep.dataforge.meta.sequence import hep.dataforge.meta.sequence
import hep.dataforge.vision.Vision import hep.dataforge.vision.Vision
import hep.dataforge.vision.ownProperties
import hep.dataforge.vision.properties import hep.dataforge.vision.properties
import hep.dataforge.vision.solid.* import hep.dataforge.vision.solid.*
@ -28,8 +27,8 @@ internal fun Vision.updateFrom(other: Vision): Vision {
scaleY = scaleY.toDouble() * other.scaleY.toDouble() scaleY = scaleY.toDouble() * other.scaleY.toDouble()
scaleZ = scaleZ.toDouble() * other.scaleZ.toDouble() scaleZ = scaleZ.toDouble() * other.scaleZ.toDouble()
} }
other.ownProperties?.sequence()?.forEach { (name, item) -> other.properties?.sequence()?.forEach { (name, item) ->
if (properties.getItem(name) == null) { if (getProperty(name) == null) {
setProperty(name, item) setProperty(name, item)
} }
} }

View File

@ -1,5 +1,6 @@
package hep.dataforge.vision.solid package hep.dataforge.vision.solid
import hep.dataforge.meta.Meta
import hep.dataforge.meta.update import hep.dataforge.meta.update
import hep.dataforge.names.NameToken import hep.dataforge.names.NameToken
import hep.dataforge.vision.* import hep.dataforge.vision.*
@ -39,7 +40,7 @@ public inline fun VisionContainerBuilder<Solid>.composite(
val children = group.children.values.filterIsInstance<Solid>() val children = group.children.values.filterIsInstance<Solid>()
if (children.size != 2) error("Composite requires exactly two children") if (children.size != 2) error("Composite requires exactly two children")
return Composite(type, children[0], children[1]).also { return Composite(type, children[0], children[1]).also {
it.configure { update(group.ownProperties) } it.configure { update(group.properties ?: Meta.EMPTY) }
if (group.position != null) { if (group.position != null) {
it.position = group.position it.position = group.position
} }

View File

@ -6,7 +6,7 @@ import hep.dataforge.names.asName
import hep.dataforge.names.plus import hep.dataforge.names.plus
import hep.dataforge.vision.VisionBuilder import hep.dataforge.vision.VisionBuilder
import hep.dataforge.vision.VisionContainerBuilder import hep.dataforge.vision.VisionContainerBuilder
import hep.dataforge.vision.props import hep.dataforge.vision.allProperties
import hep.dataforge.vision.set import hep.dataforge.vision.set
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@ -16,7 +16,7 @@ import kotlinx.serialization.Serializable
public class PolyLine(public var points: List<Point3D>) : SolidBase(), Solid { public class PolyLine(public var points: List<Point3D>) : SolidBase(), Solid {
//var lineType by string() //var lineType by string()
public var thickness: Number by props().number(1.0, key = SolidMaterial.MATERIAL_KEY + THICKNESS_KEY) public var thickness: Number by allProperties(inherit = false).number(1.0, key = SolidMaterial.MATERIAL_KEY + THICKNESS_KEY)
public companion object { public companion object {
public val THICKNESS_KEY: Name = "thickness".asName() public val THICKNESS_KEY: Name = "thickness".asName()

View File

@ -81,7 +81,7 @@ public interface Solid : Vision {
if (first.position != second.position) return false if (first.position != second.position) return false
if (first.rotation != second.rotation) return false if (first.rotation != second.rotation) return false
if (first.scale != second.scale) return false if (first.scale != second.scale) return false
if (first.ownProperties != second.ownProperties) return false if (first.properties != second.properties) return false
return true return true
} }
@ -89,7 +89,7 @@ public interface Solid : Vision {
var result = +(solid.position?.hashCode() ?: 0) var result = +(solid.position?.hashCode() ?: 0)
result = 31 * result + (solid.rotation?.hashCode() ?: 0) result = 31 * result + (solid.rotation?.hashCode() ?: 0)
result = 31 * result + (solid.scale?.hashCode() ?: 0) result = 31 * result + (solid.scale?.hashCode() ?: 0)
result = 31 * result + solid.properties.hashCode() result = 31 * result + solid.allProperties().hashCode()
return result return result
} }
} }
@ -99,7 +99,7 @@ public interface Solid : Vision {
* Get the layer number this solid belongs to. Return 0 if layer is not defined. * Get the layer number this solid belongs to. Return 0 if layer is not defined.
*/ */
public var Solid.layer: Int public var Solid.layer: Int
get() = properties.getItem(LAYER_KEY).int ?: 0 get() = allProperties().getItem(LAYER_KEY).int ?: 0
set(value) { set(value) {
setProperty(LAYER_KEY, value) setProperty(LAYER_KEY, value)
} }

View File

@ -130,7 +130,7 @@ internal class Prototypes(
includeDefaults: Boolean, includeDefaults: Boolean,
): MetaItem<*>? = null ): MetaItem<*>? = null
override fun setProperty(name: Name, item: MetaItem<*>?) { override fun setProperty(name: Name, item: MetaItem<*>?, notify: Boolean) {
TODO("Not yet implemented") TODO("Not yet implemented")
} }

View File

@ -41,14 +41,14 @@ public operator fun ColorAccessor?.invoke(webColor: String) {
* Set color as RGB integer * Set color as RGB integer
*/ */
public operator fun ColorAccessor?.invoke(rgb: Int) { public operator fun ColorAccessor?.invoke(rgb: Int) {
this?.value = rgb.asValue() this?.value = Colors.rgbToString(rgb).asValue()
} }
/** /**
* Set color as RGB * Set color as RGB
*/ */
public operator fun ColorAccessor?.invoke(r: UByte, g: UByte, b: UByte) { public operator fun ColorAccessor?.invoke(r: UByte, g: UByte, b: UByte) {
this?.value = Colors.rgbToString(r, g, b).asValue() this?.value = Colors.rgbToString(r, g, b).asValue()
} }
@VisionBuilder @VisionBuilder
@ -112,7 +112,15 @@ public class SolidMaterial : Scheme() {
} }
} }
public val Solid.color: ColorAccessor get() = ColorAccessor(properties, MATERIAL_COLOR_KEY) public val Solid.color: ColorAccessor
get() = ColorAccessor(
allProperties(
inherit = true,
includeStyles = true,
includeDefaults = true
),
MATERIAL_COLOR_KEY
)
public var Solid.material: SolidMaterial? public var Solid.material: SolidMaterial?
get() = getProperty(MATERIAL_KEY).node?.let { SolidMaterial.read(it) } get() = getProperty(MATERIAL_KEY).node?.let { SolidMaterial.read(it) }
@ -120,7 +128,11 @@ public var Solid.material: SolidMaterial?
@VisionBuilder @VisionBuilder
public fun Solid.material(builder: SolidMaterial.() -> Unit) { public fun Solid.material(builder: SolidMaterial.() -> Unit) {
val node = properties.getItem(MATERIAL_KEY).node val node = allProperties(
inherit = true,
includeStyles = true,
includeDefaults = true
).getItem(MATERIAL_KEY).node
if (node != null) { if (node != null) {
SolidMaterial.update(node, builder) SolidMaterial.update(node, builder)
} else { } else {

View File

@ -47,12 +47,12 @@ public class SolidReferenceGroup(
return getOwnProperty(childPropertyName(childName, propertyName)) return getOwnProperty(childPropertyName(childName, propertyName))
} }
private fun setChildProperty(childName: Name, propertyName: Name, item: MetaItem<*>?) { private fun setChildProperty(childName: Name, propertyName: Name, item: MetaItem<*>?, notify: Boolean) {
setProperty(childPropertyName(childName, propertyName), item) setProperty(childPropertyName(childName, propertyName), item, notify)
} }
private fun prototypeFor(name: Name): Solid { private fun prototypeFor(name: Name): Solid {
return if(name.isEmpty()) prototype else { return if (name.isEmpty()) prototype else {
(prototype as? SolidGroup)?.get(name) as? Solid (prototype as? SolidGroup)?.get(name) as? Solid
?: error("Prototype with name $name not found in $this") ?: error("Prototype with name $name not found in $this")
} }
@ -98,8 +98,8 @@ public class SolidReferenceGroup(
override fun getOwnProperty(name: Name): MetaItem<*>? = getChildProperty(childName, name) override fun getOwnProperty(name: Name): MetaItem<*>? = getChildProperty(childName, name)
override fun setProperty(name: Name, item: MetaItem<*>?) { override fun setProperty(name: Name, item: MetaItem<*>?, notify: Boolean) {
setChildProperty(childName, name, item) setChildProperty(childName, name, item, notify)
} }
override fun getProperty( override fun getProperty(
@ -119,16 +119,16 @@ public class SolidReferenceGroup(
}.merge() }.merge()
override var parent: VisionGroup? override var parent: VisionGroup?
get(){ get() {
val parentName = childName.cutLast() val parentName = childName.cutLast()
return if( parentName.isEmpty()) this@SolidReferenceGroup else ReferenceChild(parentName) return if (parentName.isEmpty()) this@SolidReferenceGroup else ReferenceChild(parentName)
} }
set(value) { set(value) {
error("Setting a parent for a reference child is not possible") error("Setting a parent for a reference child is not possible")
} }
override val propertyInvalidated: Flow<Name> override val propertyNameFlow: Flow<Name>
get() = this@SolidReferenceGroup.propertyInvalidated.filter { name -> get() = this@SolidReferenceGroup.propertyNameFlow.filter { name ->
name.startsWith(childToken(childName)) name.startsWith(childToken(childName))
}.map { name -> }.map { name ->
name.cutFirst() name.cutFirst()

View File

@ -9,7 +9,7 @@ import hep.dataforge.vision.solid.*
internal fun mergeChild(parent: VisionGroup, child: Vision): Vision { internal fun mergeChild(parent: VisionGroup, child: Vision): Vision {
return child.apply { return child.apply {
configure(parent.ownProperties) configure(parent.properties)
//parent.properties?.let { config.update(it) } //parent.properties?.let { config.update(it) }

View File

@ -2,10 +2,7 @@ package hep.dataforge.vision.solid
import hep.dataforge.meta.int import hep.dataforge.meta.int
import hep.dataforge.names.asName import hep.dataforge.names.asName
import hep.dataforge.vision.getProperty import hep.dataforge.vision.*
import hep.dataforge.vision.setProperty
import hep.dataforge.vision.styleSheet
import hep.dataforge.vision.useStyle
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals

View File

@ -4,7 +4,7 @@ import hep.dataforge.names.Name
import hep.dataforge.names.toName import hep.dataforge.names.toName
import hep.dataforge.vision.MutableVisionGroup import hep.dataforge.vision.MutableVisionGroup
import hep.dataforge.vision.get import hep.dataforge.vision.get
import hep.dataforge.vision.ownProperties import hep.dataforge.vision.properties
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -33,7 +33,7 @@ class SerializationTest {
val string = SolidManager.encodeToString(cube) val string = SolidManager.encodeToString(cube)
println(string) println(string)
val newCube = SolidManager.decodeFromString(string) val newCube = SolidManager.decodeFromString(string)
assertEquals(cube.ownProperties, newCube.ownProperties) assertEquals(cube.properties, newCube.properties)
} }
@Test @Test
@ -54,7 +54,7 @@ class SerializationTest {
val string = SolidManager.encodeToString(group) val string = SolidManager.encodeToString(group)
println(string) println(string)
val reconstructed = SolidManager.decodeFromString(string) as SolidGroup val reconstructed = SolidManager.decodeFromString(string) as SolidGroup
assertEquals(group["cube"]?.ownProperties, reconstructed["cube"]?.ownProperties) assertEquals(group["cube"]?.properties, reconstructed["cube"]?.properties)
} }
} }

View File

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

View File

@ -45,7 +45,7 @@ public abstract class MeshThreeFactory<in T : Solid>(
}.applyProperties(obj) }.applyProperties(obj)
//add listener to object properties //add listener to object properties
obj.propertyInvalidated.onEach { name -> obj.propertyNameFlow.onEach { name ->
when { when {
name.startsWith(Solid.GEOMETRY_KEY) -> { name.startsWith(Solid.GEOMETRY_KEY) -> {
val oldGeometry = mesh.geometry as BufferGeometry val oldGeometry = mesh.geometry as BufferGeometry

View File

@ -27,7 +27,7 @@ public object ThreeLabelFactory : ThreeFactory<SolidLabel> {
}) })
return Mesh(textGeo, getMaterial(obj, true)).apply { return Mesh(textGeo, getMaterial(obj, true)).apply {
updatePosition(obj) updatePosition(obj)
obj.propertyInvalidated.onEach { _ -> obj.propertyNameFlow.onEach { _ ->
//TODO //TODO
three.logger.warn{"Label parameter change not implemented"} three.logger.warn{"Label parameter change not implemented"}
}.launchIn(three.updateScope) }.launchIn(three.updateScope)

View File

@ -30,7 +30,7 @@ public object ThreeLineFactory : ThreeFactory<PolyLine> {
updatePosition(obj) updatePosition(obj)
//layers.enable(obj.layer) //layers.enable(obj.layer)
//add listener to object properties //add listener to object properties
obj.propertyInvalidated.onEach { propertyName -> obj.propertyNameFlow.onEach { propertyName ->
updateProperty(obj, propertyName) updateProperty(obj, propertyName)
}.launchIn(three.updateScope) }.launchIn(three.updateScope)
} }

View File

@ -68,7 +68,7 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
updatePosition(obj) updatePosition(obj)
//obj.onChildrenChange() //obj.onChildrenChange()
obj.propertyInvalidated.onEach { name -> obj.propertyNameFlow.onEach { name ->
if ( if (
name.startsWith(Solid.POSITION_KEY) || name.startsWith(Solid.POSITION_KEY) ||
name.startsWith(Solid.ROTATION) || name.startsWith(Solid.ROTATION) ||

View File

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