[WIP] Change property and structure subscription to flows.
This commit is contained in:
parent
6a6d9659ca
commit
97cdfd3719
@ -10,9 +10,9 @@ import hep.dataforge.vision.bootstrap.canvasControls
|
||||
import hep.dataforge.vision.bootstrap.card
|
||||
import hep.dataforge.vision.bootstrap.gridRow
|
||||
import hep.dataforge.vision.react.ThreeCanvasComponent
|
||||
import hep.dataforge.vision.react.configEditor
|
||||
import hep.dataforge.vision.react.flexColumn
|
||||
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.Canvas3DOptions
|
||||
import hep.dataforge.vision.solid.three.ThreeCanvas
|
||||
@ -193,7 +193,7 @@ val MMApp = functionalComponent<MMAppProps>("Muon monitor") { props ->
|
||||
else -> root[selected]
|
||||
}
|
||||
if (selectedObject != null) {
|
||||
configEditor(
|
||||
propertyEditor(
|
||||
selectedObject.config,
|
||||
selectedObject.descriptor,
|
||||
default = selectedObject.allProperties,
|
||||
|
@ -5,7 +5,6 @@ import hep.dataforge.names.plus
|
||||
import hep.dataforge.names.startsWith
|
||||
import hep.dataforge.values.asValue
|
||||
import hep.dataforge.vision.getProperty
|
||||
import hep.dataforge.vision.properties
|
||||
import hep.dataforge.vision.set
|
||||
import hep.dataforge.vision.setProperty
|
||||
import hep.dataforge.vision.solid.*
|
||||
@ -34,8 +33,8 @@ internal class VariableBox(xSize: Number, ySize: Number, zSize: Number) : ThreeV
|
||||
scaleX = xSize
|
||||
scaleY = ySize
|
||||
scaleZ = zSize
|
||||
properties[MeshThreeFactory.EDGES_ENABLED_KEY] = false
|
||||
properties[MeshThreeFactory.WIREFRAME_ENABLED_KEY] = false
|
||||
getProperty(MeshThreeFactory.EDGES_ENABLED_KEY, false)
|
||||
getProperty(MeshThreeFactory.WIREFRAME_ENABLED_KEY, false)
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
//add listener to object properties
|
||||
propertyInvalidated.onEach { name ->
|
||||
propertyNameFlow.onEach { name ->
|
||||
when {
|
||||
name.startsWith(GEOMETRY_KEY) -> {
|
||||
val newXSize = getProperty(X_SIZE_KEY, false).number?.toDouble() ?: 1.0
|
||||
|
@ -1,6 +1,6 @@
|
||||
pluginManagement {
|
||||
val kotlinVersion = "1.4.20"
|
||||
val toolsVersion = "0.7.0"
|
||||
val kotlinVersion = "1.4.21"
|
||||
val toolsVersion = "0.7.1"
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
|
@ -3,29 +3,30 @@ package hep.dataforge.vision.bootstrap
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.descriptors.NodeDescriptor
|
||||
import hep.dataforge.vision.Vision
|
||||
import hep.dataforge.vision.allProperties
|
||||
import hep.dataforge.vision.getStyle
|
||||
import hep.dataforge.vision.properties
|
||||
import hep.dataforge.vision.react.configEditor
|
||||
import hep.dataforge.vision.react.metaViewer
|
||||
import hep.dataforge.vision.styles
|
||||
import org.w3c.dom.Element
|
||||
import react.RBuilder
|
||||
import react.dom.render
|
||||
|
||||
public fun RBuilder.visionPropertyEditor(
|
||||
item: Vision,
|
||||
descriptor: NodeDescriptor? = item.descriptor,
|
||||
vision: Vision,
|
||||
descriptor: NodeDescriptor? = vision.descriptor,
|
||||
default: Meta? = null,
|
||||
key: Any? = null
|
||||
key: Any? = null,
|
||||
) {
|
||||
card("Properties") {
|
||||
configEditor(item.properties, descriptor, default, key)
|
||||
configEditor(vision.allProperties(), descriptor, default, key)
|
||||
}
|
||||
val styles = item.styles
|
||||
if(styles.isNotEmpty()) {
|
||||
val styles = vision.styles
|
||||
if (styles.isNotEmpty()) {
|
||||
card("Styles") {
|
||||
accordion("styles") {
|
||||
styles.forEach { styleName ->
|
||||
val style = item.getStyle(styleName)
|
||||
val style = vision.getStyle(styleName)
|
||||
if (style != null) {
|
||||
entry(styleName) {
|
||||
metaViewer(style)
|
||||
@ -40,7 +41,7 @@ public fun RBuilder.visionPropertyEditor(
|
||||
public fun Element.visionPropertyEditor(
|
||||
item: Vision,
|
||||
descriptor: NodeDescriptor? = item.descriptor,
|
||||
default: Meta? = null
|
||||
default: Meta? = null,
|
||||
): Unit = render(this) {
|
||||
visionPropertyEditor(item, descriptor, default)
|
||||
}
|
@ -7,6 +7,14 @@ import hep.dataforge.names.NameToken
|
||||
import hep.dataforge.names.lastOrNull
|
||||
import hep.dataforge.names.plus
|
||||
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 org.w3c.dom.Element
|
||||
import org.w3c.dom.events.Event
|
||||
@ -14,55 +22,59 @@ import react.*
|
||||
import react.dom.render
|
||||
import styled.*
|
||||
|
||||
public external interface ConfigEditorItemProps : RProps {
|
||||
public external interface PropertyEditorItemProps : RProps {
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
/**
|
||||
* Root default
|
||||
*/
|
||||
public var default: Meta?
|
||||
|
||||
/**
|
||||
* Root descriptor
|
||||
*/
|
||||
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 ->
|
||||
configEditorItem(props)
|
||||
propertyEditorItem(props)
|
||||
}
|
||||
|
||||
private fun RBuilder.configEditorItem(props: ConfigEditorItemProps) {
|
||||
private fun RBuilder.propertyEditorItem(props: PropertyEditorItemProps) {
|
||||
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 defaultItem = props.default?.get(props.name)
|
||||
var actualItem: MetaItem<Meta>? by useState { item ?: defaultItem ?: descriptorItem?.defaultItem() }
|
||||
var actualItem: MetaItem<Meta>? by useState { item ?: descriptorItem?.defaultItem() }
|
||||
|
||||
val token = props.name.lastOrNull()?.toString() ?: "Properties"
|
||||
|
||||
fun update() {
|
||||
item = props.root.getItem(props.name)
|
||||
actualItem = item ?: defaultItem ?: descriptorItem?.defaultItem()
|
||||
item = props.provider.getItem(props.name)
|
||||
actualItem = item ?: descriptorItem?.defaultItem()
|
||||
}
|
||||
|
||||
useEffectWithCleanup(listOf(props.root)) {
|
||||
props.root.onChange(this) { name, _, _ ->
|
||||
if (name == props.name) {
|
||||
update()
|
||||
}
|
||||
if (props.updateFlow != null) {
|
||||
useEffectWithCleanup(listOf(props.provider, props.updateFlow)) {
|
||||
val updateJob = props.updateFlow!!.onEach { updatedName ->
|
||||
if (updatedName == props.name) {
|
||||
update()
|
||||
}
|
||||
}.launchIn(props.scope ?: GlobalScope)
|
||||
return@useEffectWithCleanup { updateJob.cancel() }
|
||||
}
|
||||
return@useEffectWithCleanup { props.root.removeListener(this) }
|
||||
}
|
||||
|
||||
val expanderClick: (Event) -> Unit = {
|
||||
@ -71,15 +83,15 @@ private fun RBuilder.configEditorItem(props: ConfigEditorItemProps) {
|
||||
|
||||
val valueChanged: (Value?) -> Unit = {
|
||||
if (it == null) {
|
||||
props.root.remove(props.name)
|
||||
props.provider.remove(props.name)
|
||||
} else {
|
||||
props.root[props.name] = it
|
||||
props.provider[props.name] = it
|
||||
}
|
||||
update()
|
||||
}
|
||||
|
||||
val removeClick: (Event) -> Unit = {
|
||||
props.root.remove(props.name)
|
||||
props.provider.remove(props.name)
|
||||
update()
|
||||
}
|
||||
|
||||
@ -121,7 +133,6 @@ private fun RBuilder.configEditorItem(props: ConfigEditorItemProps) {
|
||||
add(NameToken(it))
|
||||
}
|
||||
item?.node?.items?.keys?.let { addAll(it) }
|
||||
defaultItem?.node?.items?.keys?.let { addAll(it) }
|
||||
}
|
||||
|
||||
keys.filter { !it.body.startsWith("@") }.forEach { token ->
|
||||
@ -129,12 +140,11 @@ private fun RBuilder.configEditorItem(props: ConfigEditorItemProps) {
|
||||
css {
|
||||
+TreeStyles.treeItem
|
||||
}
|
||||
child(ConfigEditorItem) {
|
||||
child(PropertyEditorItem) {
|
||||
attrs {
|
||||
this.key = props.name.toString()
|
||||
this.root = props.root
|
||||
this.provider = props.provider
|
||||
this.name = props.name + token
|
||||
this.default = props.default
|
||||
this.descriptor = props.descriptor
|
||||
}
|
||||
}
|
||||
@ -190,63 +200,79 @@ private fun RBuilder.configEditorItem(props: ConfigEditorItemProps) {
|
||||
}
|
||||
}
|
||||
|
||||
public external interface ConfigEditorProps : RProps {
|
||||
public var id: Name
|
||||
public var root: MutableItemProvider
|
||||
public var default: Meta?
|
||||
public external interface PropertyEditorProps : RProps {
|
||||
public var provider: MutableItemProvider
|
||||
public var updateFlow: Flow<Name>?
|
||||
public var descriptor: NodeDescriptor?
|
||||
public var scope: CoroutineScope?
|
||||
}
|
||||
|
||||
@JsExport
|
||||
public val ConfigEditor: FunctionalComponent<ConfigEditorProps> = functionalComponent("ConfigEditor") { props ->
|
||||
child(ConfigEditorItem) {
|
||||
public val PropertyEditor: FunctionalComponent<PropertyEditorProps> = functionalComponent("ConfigEditor") { props ->
|
||||
child(PropertyEditorItem) {
|
||||
attrs {
|
||||
this.key = ""
|
||||
this.root = props.root
|
||||
this.provider = props.provider
|
||||
this.name = Name.EMPTY
|
||||
this.default = props.default
|
||||
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(
|
||||
config: Config,
|
||||
descriptor: NodeDescriptor? = null,
|
||||
default: Meta? = null,
|
||||
key: Any? = null,
|
||||
scope: CoroutineScope? = null,
|
||||
) {
|
||||
render(this) {
|
||||
child(ConfigEditor) {
|
||||
attrs {
|
||||
this.key = key?.toString() ?: ""
|
||||
this.root = config
|
||||
this.descriptor = descriptor
|
||||
this.default = default
|
||||
}
|
||||
}
|
||||
configEditor(config,descriptor,default, key, scope)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
@ -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,
|
||||
* but instead is just a repository for named configurations.
|
||||
|
@ -1,15 +1,15 @@
|
||||
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.NodeDescriptor
|
||||
import hep.dataforge.meta.descriptors.get
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.asName
|
||||
import hep.dataforge.names.toName
|
||||
import hep.dataforge.provider.Type
|
||||
import hep.dataforge.values.asValue
|
||||
import hep.dataforge.vision.Vision.Companion.TYPE
|
||||
import hep.dataforge.vision.Vision.Companion.VISIBLE_KEY
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.serialization.Transient
|
||||
|
||||
@ -26,7 +26,8 @@ public interface Vision : Described {
|
||||
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<*>?
|
||||
|
||||
@ -46,13 +47,13 @@ public interface Vision : Described {
|
||||
/**
|
||||
* 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
|
||||
* 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
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@ -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
|
||||
get() = object : MutableItemProvider {
|
||||
override fun getItem(name: Name): MetaItem<*>? = getProperty(name,
|
||||
inherit = false,
|
||||
includeStyles = true,
|
||||
includeDefaults = true
|
||||
public fun Vision.allProperties(
|
||||
inherit: Boolean? = null,
|
||||
includeStyles: Boolean = true,
|
||||
includeDefaults: Boolean = true,
|
||||
): MutableItemProvider = object : MutableItemProvider {
|
||||
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
|
||||
*/
|
||||
@ -116,31 +111,13 @@ public fun Vision.getProperty(
|
||||
/**
|
||||
* A convenience method to pair [getProperty]
|
||||
*/
|
||||
public fun Vision.setProperty(key: Name, value: Any?) {
|
||||
properties[key] = value
|
||||
public fun Vision.setProperty(key: Name, item: Any?) {
|
||||
setProperty(key, MetaItem.of(item))
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience method to pair [getProperty]
|
||||
*/
|
||||
public fun Vision.setProperty(key: String, value: Any?) {
|
||||
properties[key] = value
|
||||
public fun Vision.setProperty(key: String, item: Any?) {
|
||||
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)
|
||||
}
|
||||
|
||||
}
|
@ -1,9 +1,12 @@
|
||||
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.defaultItem
|
||||
import hep.dataforge.meta.descriptors.get
|
||||
import hep.dataforge.meta.update
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.asName
|
||||
import hep.dataforge.values.ValueType
|
||||
@ -30,34 +33,28 @@ public open class VisionBase : Vision {
|
||||
/**
|
||||
* Object own properties excluding styles and inheritance
|
||||
*/
|
||||
@SerialName("properties")
|
||||
protected var innerProperties: Config? = null
|
||||
public var properties: Config? = null
|
||||
private set
|
||||
|
||||
/**
|
||||
* All own properties as a read-only Meta
|
||||
*/
|
||||
public val ownProperties: Meta get() = innerProperties ?: Meta.EMPTY
|
||||
|
||||
@Synchronized
|
||||
private fun getOrCreateConfig(): Config {
|
||||
if (innerProperties == null) {
|
||||
if (properties == null) {
|
||||
val newProperties = Config()
|
||||
innerProperties = newProperties
|
||||
properties = newProperties
|
||||
newProperties.onChange(this) { name, oldItem, newItem ->
|
||||
if (oldItem != newItem) {
|
||||
notifyPropertyChanged(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
return innerProperties!!
|
||||
return properties!!
|
||||
}
|
||||
|
||||
/**
|
||||
* A fast accessor method to get own property (no inheritance or styles
|
||||
*/
|
||||
override fun getOwnProperty(name: Name): MetaItem<*>? {
|
||||
return innerProperties?.getItem(name)
|
||||
return properties?.getItem(name)
|
||||
}
|
||||
|
||||
override fun getProperty(
|
||||
@ -77,9 +74,11 @@ public open class VisionBase : Vision {
|
||||
}.merge()
|
||||
|
||||
@Synchronized
|
||||
override fun setProperty(name: Name, item: MetaItem<*>?) {
|
||||
override fun setProperty(name: Name, item: MetaItem<*>?, notify: Boolean) {
|
||||
getOrCreateConfig().setItem(name, item)
|
||||
notifyPropertyChanged(name)
|
||||
if(notify) {
|
||||
notifyPropertyChanged(name)
|
||||
}
|
||||
}
|
||||
|
||||
override val descriptor: NodeDescriptor? get() = null
|
||||
@ -96,11 +95,11 @@ public open class VisionBase : Vision {
|
||||
@Transient
|
||||
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) {
|
||||
if (propertyName == STYLE_KEY) {
|
||||
updateStyles(properties.getItem(STYLE_KEY)?.stringList ?: emptyList())
|
||||
updateStyles(styles)
|
||||
}
|
||||
|
||||
_propertyInvalidationFlow.tryEmit(propertyName)
|
||||
|
@ -64,7 +64,7 @@ private fun Vision.isolate(manager: VisionManager): Vision {
|
||||
public class VisionChange(
|
||||
public val reset: Boolean = false,
|
||||
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,
|
||||
) {
|
||||
|
||||
@ -81,7 +81,7 @@ private fun CoroutineScope.collectChange(
|
||||
) {
|
||||
|
||||
//Collect properties change
|
||||
source.propertyInvalidated.onEach { propertyName ->
|
||||
source.propertyNameFlow.onEach { propertyName ->
|
||||
val newItem = source.getProperty(propertyName, inherit = false, includeStyles = false, includeDefaults = false)
|
||||
collector().propertyChanged(name, propertyName, newItem)
|
||||
}.launchIn(this)
|
||||
|
@ -31,16 +31,15 @@ public inline fun <reified E : Enum<E>> NodeDescriptor.enum(key: Name, default:
|
||||
}
|
||||
|
||||
@DFExperimental
|
||||
public val Vision.ownProperties: Meta?
|
||||
get() = (this as? VisionBase)?.ownProperties
|
||||
public val Vision.properties: Config?
|
||||
get() = (this as? VisionBase)?.properties
|
||||
|
||||
@DFExperimental
|
||||
public val Vision.describedProperties: Meta
|
||||
get() = Meta {
|
||||
descriptor?.items?.forEach { (key, _) ->
|
||||
key put getProperty(key)
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Control visibility of the element
|
||||
*/
|
||||
public var Vision.visible: Boolean?
|
||||
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))
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -19,8 +19,8 @@ class VisualObjectEditorFragment(val selector: (Vision) -> Meta) : Fragment() {
|
||||
constructor(
|
||||
item: Vision?,
|
||||
descriptor: NodeDescriptor?,
|
||||
selector: (Vision) -> MutableItemProvider = { it.properties }
|
||||
) : this({it.describedProperties}) {
|
||||
selector: (Vision) -> MutableItemProvider = { it.allProperties() },
|
||||
) : this({ it.describedProperties }) {
|
||||
this.item = item
|
||||
this.descriptorProperty.set(descriptor)
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ class FXReferenceFactory(val plugin: FX3DPlugin) : FX3DFactory<SolidReferenceGro
|
||||
val prototype = obj.prototype
|
||||
val node = plugin.buildNode(prototype)
|
||||
|
||||
obj.propertyInvalidated.onEach { name->
|
||||
obj.propertyNameFlow.onEach { name->
|
||||
if (name.firstOrNull()?.body == SolidReferenceGroup.REFERENCE_CHILD_PROPERTY_PREFIX) {
|
||||
val childName = name.firstOrNull()?.index?.toName() ?: error("Wrong syntax for reference child property: '$name'")
|
||||
val propertyName = name.cutFirst()
|
||||
|
@ -18,7 +18,7 @@ class VisualObjectFXBinding(val fx: FX3DPlugin, val obj: Vision) {
|
||||
private val bindings = HashMap<Name, ObjectBinding<MetaItem<*>?>>()
|
||||
|
||||
init {
|
||||
obj.propertyInvalidated.onEach { name ->
|
||||
obj.propertyNameFlow.onEach { name ->
|
||||
bindings.filter { it.key.startsWith(name) }.forEach { entry ->
|
||||
Platform.runLater {
|
||||
entry.value.invalidate()
|
||||
|
@ -38,7 +38,6 @@ public class GDMLTransformerSettings {
|
||||
|
||||
public var solidAction: (GDMLSolid) -> Action = { Action.PROTOTYPE }
|
||||
public var volumeAction: (GDMLGroup) -> Action = { Action.PROTOTYPE }
|
||||
|
||||
}
|
||||
|
||||
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.useStyle("GDML") {
|
||||
Solid.ROTATION_ORDER_KEY put RotationOrder.ZXY
|
||||
|
@ -3,7 +3,6 @@ package hep.dataforge.vision.gdml
|
||||
import hep.dataforge.meta.DFExperimental
|
||||
import hep.dataforge.meta.sequence
|
||||
import hep.dataforge.vision.Vision
|
||||
import hep.dataforge.vision.ownProperties
|
||||
import hep.dataforge.vision.properties
|
||||
import hep.dataforge.vision.solid.*
|
||||
|
||||
@ -28,8 +27,8 @@ internal fun Vision.updateFrom(other: Vision): Vision {
|
||||
scaleY = scaleY.toDouble() * other.scaleY.toDouble()
|
||||
scaleZ = scaleZ.toDouble() * other.scaleZ.toDouble()
|
||||
}
|
||||
other.ownProperties?.sequence()?.forEach { (name, item) ->
|
||||
if (properties.getItem(name) == null) {
|
||||
other.properties?.sequence()?.forEach { (name, item) ->
|
||||
if (getProperty(name) == null) {
|
||||
setProperty(name, item)
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package hep.dataforge.vision.solid
|
||||
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.update
|
||||
import hep.dataforge.names.NameToken
|
||||
import hep.dataforge.vision.*
|
||||
@ -39,7 +40,7 @@ public inline fun VisionContainerBuilder<Solid>.composite(
|
||||
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 {
|
||||
it.configure { update(group.ownProperties) }
|
||||
it.configure { update(group.properties ?: Meta.EMPTY) }
|
||||
if (group.position != null) {
|
||||
it.position = group.position
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import hep.dataforge.names.asName
|
||||
import hep.dataforge.names.plus
|
||||
import hep.dataforge.vision.VisionBuilder
|
||||
import hep.dataforge.vision.VisionContainerBuilder
|
||||
import hep.dataforge.vision.props
|
||||
import hep.dataforge.vision.allProperties
|
||||
import hep.dataforge.vision.set
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
@ -16,7 +16,7 @@ import kotlinx.serialization.Serializable
|
||||
public class PolyLine(public var points: List<Point3D>) : SolidBase(), Solid {
|
||||
|
||||
//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 val THICKNESS_KEY: Name = "thickness".asName()
|
||||
|
@ -81,7 +81,7 @@ public interface Solid : Vision {
|
||||
if (first.position != second.position) return false
|
||||
if (first.rotation != second.rotation) 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
|
||||
}
|
||||
|
||||
@ -89,7 +89,7 @@ public interface Solid : Vision {
|
||||
var result = +(solid.position?.hashCode() ?: 0)
|
||||
result = 31 * result + (solid.rotation?.hashCode() ?: 0)
|
||||
result = 31 * result + (solid.scale?.hashCode() ?: 0)
|
||||
result = 31 * result + solid.properties.hashCode()
|
||||
result = 31 * result + solid.allProperties().hashCode()
|
||||
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.
|
||||
*/
|
||||
public var Solid.layer: Int
|
||||
get() = properties.getItem(LAYER_KEY).int ?: 0
|
||||
get() = allProperties().getItem(LAYER_KEY).int ?: 0
|
||||
set(value) {
|
||||
setProperty(LAYER_KEY, value)
|
||||
}
|
||||
|
@ -130,7 +130,7 @@ internal class Prototypes(
|
||||
includeDefaults: Boolean,
|
||||
): MetaItem<*>? = null
|
||||
|
||||
override fun setProperty(name: Name, item: MetaItem<*>?) {
|
||||
override fun setProperty(name: Name, item: MetaItem<*>?, notify: Boolean) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
|
@ -41,14 +41,14 @@ public operator fun ColorAccessor?.invoke(webColor: String) {
|
||||
* Set color as RGB integer
|
||||
*/
|
||||
public operator fun ColorAccessor?.invoke(rgb: Int) {
|
||||
this?.value = rgb.asValue()
|
||||
this?.value = Colors.rgbToString(rgb).asValue()
|
||||
}
|
||||
|
||||
/**
|
||||
* Set color as RGB
|
||||
*/
|
||||
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
|
||||
@ -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?
|
||||
get() = getProperty(MATERIAL_KEY).node?.let { SolidMaterial.read(it) }
|
||||
@ -120,7 +128,11 @@ public var Solid.material: SolidMaterial?
|
||||
|
||||
@VisionBuilder
|
||||
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) {
|
||||
SolidMaterial.update(node, builder)
|
||||
} else {
|
||||
|
@ -47,12 +47,12 @@ public class SolidReferenceGroup(
|
||||
return getOwnProperty(childPropertyName(childName, propertyName))
|
||||
}
|
||||
|
||||
private fun setChildProperty(childName: Name, propertyName: Name, item: MetaItem<*>?) {
|
||||
setProperty(childPropertyName(childName, propertyName), item)
|
||||
private fun setChildProperty(childName: Name, propertyName: Name, item: MetaItem<*>?, notify: Boolean) {
|
||||
setProperty(childPropertyName(childName, propertyName), item, notify)
|
||||
}
|
||||
|
||||
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
|
||||
?: 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 setProperty(name: Name, item: MetaItem<*>?) {
|
||||
setChildProperty(childName, name, item)
|
||||
override fun setProperty(name: Name, item: MetaItem<*>?, notify: Boolean) {
|
||||
setChildProperty(childName, name, item, notify)
|
||||
}
|
||||
|
||||
override fun getProperty(
|
||||
@ -119,16 +119,16 @@ public class SolidReferenceGroup(
|
||||
}.merge()
|
||||
|
||||
override var parent: VisionGroup?
|
||||
get(){
|
||||
get() {
|
||||
val parentName = childName.cutLast()
|
||||
return if( parentName.isEmpty()) this@SolidReferenceGroup else ReferenceChild(parentName)
|
||||
return if (parentName.isEmpty()) this@SolidReferenceGroup else ReferenceChild(parentName)
|
||||
}
|
||||
set(value) {
|
||||
error("Setting a parent for a reference child is not possible")
|
||||
}
|
||||
|
||||
override val propertyInvalidated: Flow<Name>
|
||||
get() = this@SolidReferenceGroup.propertyInvalidated.filter { name ->
|
||||
override val propertyNameFlow: Flow<Name>
|
||||
get() = this@SolidReferenceGroup.propertyNameFlow.filter { name ->
|
||||
name.startsWith(childToken(childName))
|
||||
}.map { name ->
|
||||
name.cutFirst()
|
||||
|
@ -9,7 +9,7 @@ import hep.dataforge.vision.solid.*
|
||||
internal fun mergeChild(parent: VisionGroup, child: Vision): Vision {
|
||||
return child.apply {
|
||||
|
||||
configure(parent.ownProperties)
|
||||
configure(parent.properties)
|
||||
|
||||
//parent.properties?.let { config.update(it) }
|
||||
|
||||
|
@ -2,10 +2,7 @@ package hep.dataforge.vision.solid
|
||||
|
||||
import hep.dataforge.meta.int
|
||||
import hep.dataforge.names.asName
|
||||
import hep.dataforge.vision.getProperty
|
||||
import hep.dataforge.vision.setProperty
|
||||
import hep.dataforge.vision.styleSheet
|
||||
import hep.dataforge.vision.useStyle
|
||||
import hep.dataforge.vision.*
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
|
@ -4,7 +4,7 @@ import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.toName
|
||||
import hep.dataforge.vision.MutableVisionGroup
|
||||
import hep.dataforge.vision.get
|
||||
import hep.dataforge.vision.ownProperties
|
||||
import hep.dataforge.vision.properties
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
@ -33,7 +33,7 @@ class SerializationTest {
|
||||
val string = SolidManager.encodeToString(cube)
|
||||
println(string)
|
||||
val newCube = SolidManager.decodeFromString(string)
|
||||
assertEquals(cube.ownProperties, newCube.ownProperties)
|
||||
assertEquals(cube.properties, newCube.properties)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -54,7 +54,7 @@ class SerializationTest {
|
||||
val string = SolidManager.encodeToString(group)
|
||||
println(string)
|
||||
val reconstructed = SolidManager.decodeFromString(string) as SolidGroup
|
||||
assertEquals(group["cube"]?.ownProperties, reconstructed["cube"]?.ownProperties)
|
||||
assertEquals(group["cube"]?.properties, reconstructed["cube"]?.properties)
|
||||
}
|
||||
|
||||
}
|
@ -29,7 +29,7 @@ class VisionUpdateTest {
|
||||
targetVision.update(dif)
|
||||
assertTrue { targetVision["top"] is SolidGroup }
|
||||
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
|
||||
|
@ -45,7 +45,7 @@ public abstract class MeshThreeFactory<in T : Solid>(
|
||||
}.applyProperties(obj)
|
||||
|
||||
//add listener to object properties
|
||||
obj.propertyInvalidated.onEach { name ->
|
||||
obj.propertyNameFlow.onEach { name ->
|
||||
when {
|
||||
name.startsWith(Solid.GEOMETRY_KEY) -> {
|
||||
val oldGeometry = mesh.geometry as BufferGeometry
|
||||
|
@ -27,7 +27,7 @@ public object ThreeLabelFactory : ThreeFactory<SolidLabel> {
|
||||
})
|
||||
return Mesh(textGeo, getMaterial(obj, true)).apply {
|
||||
updatePosition(obj)
|
||||
obj.propertyInvalidated.onEach { _ ->
|
||||
obj.propertyNameFlow.onEach { _ ->
|
||||
//TODO
|
||||
three.logger.warn{"Label parameter change not implemented"}
|
||||
}.launchIn(three.updateScope)
|
||||
|
@ -30,7 +30,7 @@ public object ThreeLineFactory : ThreeFactory<PolyLine> {
|
||||
updatePosition(obj)
|
||||
//layers.enable(obj.layer)
|
||||
//add listener to object properties
|
||||
obj.propertyInvalidated.onEach { propertyName ->
|
||||
obj.propertyNameFlow.onEach { propertyName ->
|
||||
updateProperty(obj, propertyName)
|
||||
}.launchIn(three.updateScope)
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
|
||||
updatePosition(obj)
|
||||
//obj.onChildrenChange()
|
||||
|
||||
obj.propertyInvalidated.onEach { name ->
|
||||
obj.propertyNameFlow.onEach { name ->
|
||||
if (
|
||||
name.startsWith(Solid.POSITION_KEY) ||
|
||||
name.startsWith(Solid.ROTATION) ||
|
||||
|
@ -47,7 +47,7 @@ public object ThreeReferenceFactory : ThreeFactory<SolidReferenceGroup> {
|
||||
|
||||
//TODO apply child properties
|
||||
|
||||
obj.propertyInvalidated.onEach { name->
|
||||
obj.propertyNameFlow.onEach { name->
|
||||
if (name.firstOrNull()?.body == REFERENCE_CHILD_PROPERTY_PREFIX) {
|
||||
val childName = name.firstOrNull()?.index?.toName() ?: error("Wrong syntax for reference child property: '$name'")
|
||||
val propertyName = name.cutFirst()
|
||||
|
Loading…
Reference in New Issue
Block a user