[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.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,

View File

@ -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

View File

@ -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()

View File

@ -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
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)
}

View File

@ -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) {
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)

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,
* but instead is just a repository for named configurations.

View File

@ -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,15 +77,23 @@ 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)
}
@ -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
}
/**
* 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)
}
public fun Vision.setProperty(key: String, item: Any?) {
setProperty(key.toName(), MetaItem.of(item))
}

View File

@ -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,10 +74,12 @@ 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)
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)

View File

@ -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)

View File

@ -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))

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

View File

@ -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()

View File

@ -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()

View File

@ -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

View File

@ -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)
}
}

View File

@ -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
}

View File

@ -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()

View File

@ -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)
}

View File

@ -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")
}

View File

@ -41,7 +41,7 @@ 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()
}
/**
@ -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 {

View File

@ -47,8 +47,8 @@ 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 {
@ -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(
@ -127,8 +127,8 @@ public class SolidReferenceGroup(
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()

View File

@ -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) }

View File

@ -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

View File

@ -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)
}
}

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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)
}

View File

@ -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) ||

View File

@ -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()