forked from kscience/visionforge
Change property and structure subscription to flows.
This commit is contained in:
parent
85bb690699
commit
b2ba92e745
@ -4,13 +4,14 @@
|
|||||||
### Added
|
### Added
|
||||||
- Server module
|
- Server module
|
||||||
- Change collector
|
- Change collector
|
||||||
|
- Customizable accessors for colors
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Vision does not implement ItemProvider anymore. Property changes are done via `getProperty`/`setProperty` and `property` delegate.
|
- Vision does not implement ItemProvider anymore. Property changes are done via `getProperty`/`setProperty` and `property` delegate.
|
||||||
- Point3D and Point2D are made separate classes instead of expect/actual (to split up different engines.
|
- Point3D and Point2D are made separate classes instead of expect/actual (to split up different engines.
|
||||||
- JavaFX support moved to a separate module
|
- JavaFX support moved to a separate module
|
||||||
- Threejs support moved to a separate module
|
- Threejs support moved to a separate module
|
||||||
- \[Breaking change!\] Stylesheets are moved into properties under `@stylesheet` key
|
- \[Format breaking change!\] Stylesheets are moved into properties under `@stylesheet` key
|
||||||
|
|
||||||
### Deprecated
|
### Deprecated
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package ru.mipt.npm.muon.monitor
|
package ru.mipt.npm.muon.monitor
|
||||||
|
|
||||||
import hep.dataforge.vision.removeAll
|
import hep.dataforge.vision.removeAll
|
||||||
import hep.dataforge.vision.setProperty
|
|
||||||
import hep.dataforge.vision.solid.*
|
import hep.dataforge.vision.solid.*
|
||||||
import ru.mipt.npm.muon.monitor.Monitor.CENTRAL_LAYER_Z
|
import ru.mipt.npm.muon.monitor.Monitor.CENTRAL_LAYER_Z
|
||||||
import ru.mipt.npm.muon.monitor.Monitor.LOWER_LAYER_Z
|
import ru.mipt.npm.muon.monitor.Monitor.LOWER_LAYER_Z
|
||||||
@ -63,7 +62,6 @@ class Model {
|
|||||||
|
|
||||||
fun reset() {
|
fun reset() {
|
||||||
map.values.forEach {
|
map.values.forEach {
|
||||||
it.config
|
|
||||||
it.setProperty(SolidMaterial.MATERIAL_COLOR_KEY, null)
|
it.setProperty(SolidMaterial.MATERIAL_COLOR_KEY, null)
|
||||||
}
|
}
|
||||||
tracks.removeAll()
|
tracks.removeAll()
|
||||||
|
@ -11,7 +11,7 @@ import hep.dataforge.names.plus
|
|||||||
*/
|
*/
|
||||||
public inline class StyleSheet(private val owner: VisionGroup) {
|
public inline class StyleSheet(private val owner: VisionGroup) {
|
||||||
|
|
||||||
private val styleNode get() = owner.properties?.get(STYLESHEET_KEY).node
|
private val styleNode get() = owner.getOwnProperty(STYLESHEET_KEY).node
|
||||||
|
|
||||||
public val items: Map<NameToken, Meta>? get() = styleNode?.items?.mapValues { it.value.node ?: Meta.EMPTY }
|
public val items: Map<NameToken, Meta>? get() = styleNode?.items?.mapValues { it.value.node ?: Meta.EMPTY }
|
||||||
|
|
||||||
@ -21,11 +21,7 @@ public inline class StyleSheet(private val owner: VisionGroup) {
|
|||||||
* Define a style without notifying owner
|
* Define a style without notifying owner
|
||||||
*/
|
*/
|
||||||
public fun define(key: String, style: Meta?) {
|
public fun define(key: String, style: Meta?) {
|
||||||
if (style == null) {
|
owner.setProperty(STYLESHEET_KEY + key, style)
|
||||||
styleNode?.remove(key)
|
|
||||||
} else {
|
|
||||||
owner.config[STYLESHEET_KEY + key] = style
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -58,7 +54,7 @@ internal fun Vision.styleChanged(key: String, oldStyle: Meta?, newStyle: Meta?)
|
|||||||
val tokens: Collection<Name> =
|
val tokens: Collection<Name> =
|
||||||
((oldStyle?.items?.keys ?: emptySet()) + (newStyle?.items?.keys ?: emptySet()))
|
((oldStyle?.items?.keys ?: emptySet()) + (newStyle?.items?.keys ?: emptySet()))
|
||||||
.map { it.asName() }
|
.map { it.asName() }
|
||||||
tokens.forEach { parent?.propertyChanged(it) }
|
tokens.forEach { parent?.notifyPropertyChanged(it) }
|
||||||
}
|
}
|
||||||
if (this is VisionGroup) {
|
if (this is VisionGroup) {
|
||||||
for (obj in this) {
|
for (obj in this) {
|
||||||
@ -78,7 +74,7 @@ public val VisionGroup.styleSheet: StyleSheet get() = StyleSheet(this)
|
|||||||
* Add style name to the list of styles to be resolved later. The style with given name does not necessary exist at the moment.
|
* Add style name to the list of styles to be resolved later. The style with given name does not necessary exist at the moment.
|
||||||
*/
|
*/
|
||||||
public fun Vision.useStyle(name: String) {
|
public fun Vision.useStyle(name: String) {
|
||||||
styles = (properties[Vision.STYLE_KEY]?.stringList ?: emptyList()) + name
|
styles = (getOwnProperty(Vision.STYLE_KEY)?.stringList ?: emptyList()) + name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -86,7 +82,7 @@ public fun Vision.useStyle(name: String) {
|
|||||||
* Find a style with given name for given [Vision]. The style is not necessary applied to this [Vision].
|
* Find a style with given name for given [Vision]. The style is not necessary applied to this [Vision].
|
||||||
*/
|
*/
|
||||||
public tailrec fun Vision.getStyle(name: String): Meta? =
|
public tailrec fun Vision.getStyle(name: String): Meta? =
|
||||||
properties?.get(StyleSheet.STYLESHEET_KEY + name).node ?: parent?.getStyle(name)
|
getOwnProperty(StyleSheet.STYLESHEET_KEY + name).node ?: parent?.getStyle(name)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve an item in all style layers
|
* Resolve an item in all style layers
|
||||||
|
@ -10,13 +10,14 @@ import hep.dataforge.provider.Type
|
|||||||
import hep.dataforge.values.asValue
|
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 hep.dataforge.vision.Vision.Companion.VISIBLE_KEY
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.serialization.Transient
|
import kotlinx.serialization.Transient
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A root type for display hierarchy
|
* A root type for display hierarchy
|
||||||
*/
|
*/
|
||||||
@Type(TYPE)
|
@Type(TYPE)
|
||||||
public interface Vision : Configurable, Described {
|
public interface Vision : Described {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The parent object of this one. If null, this one is a root.
|
* The parent object of this one. If null, this one is a root.
|
||||||
@ -25,42 +26,52 @@ public interface Vision : Configurable, Described {
|
|||||||
public var parent: VisionGroup?
|
public var parent: VisionGroup?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Nullable version of [config] used to check if this [Vision] has custom properties
|
* A fast accessor method to get own property (no inheritance or styles
|
||||||
*/
|
*/
|
||||||
public val properties: Config?
|
public fun getOwnProperty(name: Name): MetaItem<*>?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All properties including styles and prototypes if present, including inherited ones
|
* Get property.
|
||||||
|
* @param inherit toggles parent node property lookup
|
||||||
|
* @param includeStyles toggles inclusion of
|
||||||
*/
|
*/
|
||||||
public val allProperties: Laminate
|
public fun getProperty(
|
||||||
|
name: Name,
|
||||||
|
inherit: Boolean = true,
|
||||||
|
includeStyles: Boolean = true,
|
||||||
|
includeDefaults: Boolean = true,
|
||||||
|
): MetaItem<*>?
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get property (including styles). [inherit] toggles parent node property lookup
|
* Set the property value
|
||||||
*/
|
*/
|
||||||
public fun getProperty(name: Name, inherit: Boolean = true): MetaItem<*>?
|
public fun setProperty(name: Name, item: MetaItem<*>?)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Trigger property invalidation event. If [name] is empty, notify that the whole object is changed
|
* Flow of property invalidation events. It does not contain property values after invalidation since it is not clear
|
||||||
|
* if it should include inherited properties etc.
|
||||||
*/
|
*/
|
||||||
public fun propertyChanged(name: Name): Unit
|
public val propertyInvalidated: Flow<Name>
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add listener triggering on property change
|
* Notify all listeners that a property has been changed and should be invalidated
|
||||||
*/
|
*/
|
||||||
public fun onPropertyChange(owner: Any?, action: (Name) -> Unit): Unit
|
public fun notifyPropertyChanged(propertyName: Name): Unit
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove change listeners with given owner.
|
|
||||||
*/
|
|
||||||
public fun removeChangeListener(owner: Any?)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of names of styles applied to this object. Order matters. Not inherited.
|
* List of names of styles applied to this object. Order matters. Not inherited.
|
||||||
*/
|
*/
|
||||||
public var styles: List<String>
|
public var styles: List<String>
|
||||||
get() = properties[STYLE_KEY]?.stringList ?: emptyList()
|
get() = getProperty(
|
||||||
|
STYLE_KEY,
|
||||||
|
inherit = false,
|
||||||
|
includeStyles = false,
|
||||||
|
includeDefaults = true
|
||||||
|
)?.stringList ?: emptyList()
|
||||||
set(value) {
|
set(value) {
|
||||||
config[STYLE_KEY] = value
|
setProperty(STYLE_KEY, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -78,24 +89,42 @@ public interface Vision : Configurable, Described {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenient accessor for all properties of a vision. Provided properties include styles and defaults, but do not inherit.
|
||||||
|
*/
|
||||||
|
public val Vision.properties: MutableItemProvider
|
||||||
|
get() = object : MutableItemProvider {
|
||||||
|
override fun getItem(name: Name): MetaItem<*>? = getProperty(name,
|
||||||
|
inherit = false,
|
||||||
|
includeStyles = true,
|
||||||
|
includeDefaults = true
|
||||||
|
)
|
||||||
|
|
||||||
|
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
|
||||||
*/
|
*/
|
||||||
public fun Vision.getProperty(key: String, inherit: Boolean = true): MetaItem<*>? =
|
public fun Vision.getProperty(
|
||||||
getProperty(key.toName(), inherit)
|
key: String,
|
||||||
|
inherit: Boolean = true,
|
||||||
|
includeStyles: Boolean = true,
|
||||||
|
includeDefaults: Boolean = true,
|
||||||
|
): MetaItem<*>? = getProperty(key.toName(), inherit, includeStyles, includeDefaults)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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, value: Any?) {
|
||||||
config[key] = value
|
properties[key] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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, value: Any?) {
|
||||||
config[key] = value
|
properties[key] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -103,27 +132,7 @@ public fun Vision.setProperty(key: String, value: Any?) {
|
|||||||
*/
|
*/
|
||||||
public var Vision.visible: Boolean?
|
public var Vision.visible: Boolean?
|
||||||
get() = getProperty(VISIBLE_KEY).boolean
|
get() = getProperty(VISIBLE_KEY).boolean
|
||||||
set(value) = config.setValue(VISIBLE_KEY, value?.asValue())
|
set(value) = setProperty(VISIBLE_KEY, value?.asValue())
|
||||||
|
|
||||||
///**
|
|
||||||
// * Convenience delegate for properties
|
|
||||||
// */
|
|
||||||
//public fun Vision.property(
|
|
||||||
// default: MetaItem<*>? = null,
|
|
||||||
// key: Name? = null,
|
|
||||||
// inherit: Boolean = true,
|
|
||||||
//): MutableItemDelegate =
|
|
||||||
// object : ReadWriteProperty<Any?, MetaItem<*>?> {
|
|
||||||
// override fun getValue(thisRef: Any?, property: KProperty<*>): MetaItem<*>? {
|
|
||||||
// val name = key ?: property.name.toName()
|
|
||||||
// return getProperty(name, inherit) ?: default
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// override fun setValue(thisRef: Any?, property: KProperty<*>, value: MetaItem<*>?) {
|
|
||||||
// val name = key ?: property.name.toName()
|
|
||||||
// setProperty(name, value)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
public fun Vision.props(inherit: Boolean = true): MutableItemProvider = object : MutableItemProvider {
|
public fun Vision.props(inherit: Boolean = true): MutableItemProvider = object : MutableItemProvider {
|
||||||
override fun getItem(name: Name): MetaItem<*>? {
|
override fun getItem(name: Name): MetaItem<*>? {
|
||||||
|
@ -3,15 +3,18 @@ package hep.dataforge.vision
|
|||||||
import hep.dataforge.meta.*
|
import hep.dataforge.meta.*
|
||||||
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.defaultMeta
|
|
||||||
import hep.dataforge.meta.descriptors.get
|
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.values.ValueType
|
import hep.dataforge.values.ValueType
|
||||||
import hep.dataforge.vision.Vision.Companion.STYLE_KEY
|
import hep.dataforge.vision.Vision.Companion.STYLE_KEY
|
||||||
|
import kotlinx.coroutines.channels.BufferOverflow
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
import kotlinx.coroutines.flow.SharedFlow
|
||||||
import kotlinx.serialization.SerialName
|
import kotlinx.serialization.SerialName
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.Transient
|
import kotlinx.serialization.Transient
|
||||||
|
import kotlin.jvm.Synchronized
|
||||||
|
|
||||||
internal data class PropertyListener(
|
internal data class PropertyListener(
|
||||||
val owner: Any? = null,
|
val owner: Any? = null,
|
||||||
@ -28,83 +31,89 @@ public open class VisionBase : Vision {
|
|||||||
/**
|
/**
|
||||||
* Object own properties excluding styles and inheritance
|
* Object own properties excluding styles and inheritance
|
||||||
*/
|
*/
|
||||||
override var properties: Config? = null
|
@SerialName("properties")
|
||||||
protected set
|
private var _properties: Config? = null
|
||||||
|
|
||||||
override val descriptor: NodeDescriptor? get() = null
|
/**
|
||||||
|
* All own properties as a read-only Meta
|
||||||
|
*/
|
||||||
|
public val ownProperties: Meta get() = _properties?: Meta.EMPTY
|
||||||
|
|
||||||
protected fun updateStyles(names: List<String>) {
|
@Synchronized
|
||||||
names.mapNotNull { getStyle(it) }.asSequence()
|
private fun getOrCreateConfig(): Config {
|
||||||
.flatMap { it.items.asSequence() }
|
if (_properties == null) {
|
||||||
.distinctBy { it.key }
|
val newProperties = Config()
|
||||||
.forEach {
|
_properties = newProperties
|
||||||
propertyChanged(it.key.asName())
|
newProperties.onChange(this) { name, oldItem, newItem ->
|
||||||
|
if (oldItem != newItem) {
|
||||||
|
notifyPropertyChanged(name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return _properties!!
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The config is initialized and assigned on-demand.
|
* A fast accessor method to get own property (no inheritance or styles
|
||||||
* To avoid unnecessary allocations, one should access [getAllProperties] via [getProperty] instead.
|
|
||||||
*/
|
*/
|
||||||
override val config: Config by lazy {
|
override fun getOwnProperty(name: Name): MetaItem<*>? {
|
||||||
properties ?: Config().also { config ->
|
return _properties?.getItem(name)
|
||||||
properties = config.also {
|
}
|
||||||
it.onChange(this) { name, _, _ -> propertyChanged(name) }
|
|
||||||
}
|
override fun getProperty(
|
||||||
|
name: Name,
|
||||||
|
inherit: Boolean,
|
||||||
|
includeStyles: Boolean,
|
||||||
|
includeDefaults: Boolean,
|
||||||
|
): MetaItem<*>? = sequence {
|
||||||
|
yield(getOwnProperty(name))
|
||||||
|
if (includeStyles) {
|
||||||
|
yieldAll(getStyleItems(name))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Transient
|
|
||||||
private val listeners = HashSet<PropertyListener>()
|
|
||||||
|
|
||||||
override fun propertyChanged(name: Name) {
|
|
||||||
if (name == STYLE_KEY) {
|
|
||||||
updateStyles(properties?.get(STYLE_KEY)?.stringList ?: emptyList())
|
|
||||||
}
|
|
||||||
for (listener in listeners) {
|
|
||||||
listener.action(name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPropertyChange(owner: Any?, action: (Name) -> Unit) {
|
|
||||||
listeners.add(PropertyListener(owner, action))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun removeChangeListener(owner: Any?) {
|
|
||||||
listeners.removeAll { owner == null || it.owner == owner }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* All available properties in a layered form
|
|
||||||
*/
|
|
||||||
override val allProperties: Laminate
|
|
||||||
get() = Laminate(
|
|
||||||
properties,
|
|
||||||
allStyles,
|
|
||||||
parent?.allProperties,
|
|
||||||
descriptor?.defaultMeta(),
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? = sequence {
|
|
||||||
yield(properties?.get(name))
|
|
||||||
yieldAll(getStyleItems(name))
|
|
||||||
if (inherit) {
|
if (inherit) {
|
||||||
yield(parent?.getProperty(name, inherit))
|
yield(parent?.getProperty(name, inherit))
|
||||||
}
|
}
|
||||||
yield(descriptor?.get(name)?.defaultItem())
|
yield(descriptor?.get(name)?.defaultItem())
|
||||||
}.merge()
|
}.merge()
|
||||||
|
|
||||||
/**
|
@Synchronized
|
||||||
* Reset all properties to their default values
|
override fun setProperty(name: Name, item: MetaItem<*>?) {
|
||||||
*/
|
getOrCreateConfig().setItem(name, item)
|
||||||
public fun resetProperties() {
|
notifyPropertyChanged(name)
|
||||||
properties?.removeListener(this)
|
}
|
||||||
properties = null
|
|
||||||
|
override val descriptor: NodeDescriptor? get() = null
|
||||||
|
|
||||||
|
private fun updateStyles(names: List<String>) {
|
||||||
|
names.mapNotNull { getStyle(it) }.asSequence()
|
||||||
|
.flatMap { it.items.asSequence() }
|
||||||
|
.distinctBy { it.key }
|
||||||
|
.forEach {
|
||||||
|
notifyPropertyChanged(it.key.asName())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val _propertyInvalidationFlow: MutableSharedFlow<Name> = MutableSharedFlow(
|
||||||
|
onBufferOverflow = BufferOverflow.DROP_OLDEST
|
||||||
|
)
|
||||||
|
|
||||||
|
override val propertyInvalidated: SharedFlow<Name> get() = _propertyInvalidationFlow
|
||||||
|
|
||||||
|
override fun notifyPropertyChanged(propertyName: Name) {
|
||||||
|
if (propertyName == STYLE_KEY) {
|
||||||
|
updateStyles(properties.getItem(STYLE_KEY)?.stringList ?: emptyList())
|
||||||
|
}
|
||||||
|
|
||||||
|
_propertyInvalidationFlow.tryEmit(propertyName)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun configure(block: MutableMeta<*>.() -> Unit) {
|
||||||
|
getOrCreateConfig().block()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun update(change: VisionChange) {
|
override fun update(change: VisionChange) {
|
||||||
change.propertyChange[Name.EMPTY]?.let {
|
change.properties?.let {
|
||||||
config.update(it)
|
getOrCreateConfig().update(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,10 @@ import hep.dataforge.names.plus
|
|||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.flow
|
import kotlinx.coroutines.flow.flow
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.serialization.*
|
import kotlinx.serialization.*
|
||||||
|
import kotlin.jvm.Synchronized
|
||||||
import kotlin.time.Duration
|
import kotlin.time.Duration
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -14,29 +17,41 @@ import kotlin.time.Duration
|
|||||||
*/
|
*/
|
||||||
public class VisionChangeBuilder : VisionContainerBuilder<Vision> {
|
public class VisionChangeBuilder : VisionContainerBuilder<Vision> {
|
||||||
|
|
||||||
private val propertyChange = HashMap<Name, Config>()
|
private var reset: Boolean = false
|
||||||
private val childrenChange = HashMap<Name, Vision?>()
|
private var vision: Vision? = null
|
||||||
|
private val propertyChange = Config()
|
||||||
|
private val children: HashMap<Name, VisionChangeBuilder> = HashMap()
|
||||||
|
|
||||||
public fun isEmpty(): Boolean = propertyChange.isEmpty() && childrenChange.isEmpty()
|
public fun isEmpty(): Boolean = propertyChange.isEmpty() && propertyChange.isEmpty() && children.isEmpty()
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
private fun getOrPutChild(visionName: Name): VisionChangeBuilder =
|
||||||
|
children.getOrPut(visionName) { VisionChangeBuilder() }
|
||||||
|
|
||||||
public fun propertyChanged(visionName: Name, propertyName: Name, item: MetaItem<*>?) {
|
public fun propertyChanged(visionName: Name, propertyName: Name, item: MetaItem<*>?) {
|
||||||
propertyChange
|
if (visionName == Name.EMPTY) {
|
||||||
.getOrPut(visionName) { Config() }
|
propertyChange[propertyName] = item
|
||||||
.setItem(propertyName, item)
|
} else {
|
||||||
|
getOrPutChild(visionName).propertyChanged(Name.EMPTY, propertyName, item)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun set(name: Name, child: Vision?) {
|
override fun set(name: Name, child: Vision?) {
|
||||||
childrenChange[name] = child
|
getOrPutChild(name).apply {
|
||||||
|
vision = child
|
||||||
|
reset = vision == null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Isolate collected changes by creating detached copies of given visions
|
* Isolate collected changes by creating detached copies of given visions
|
||||||
*/
|
*/
|
||||||
public fun isolate(manager: VisionManager): VisionChange = VisionChange(
|
public fun isolate(manager: VisionManager): VisionChange = VisionChange(
|
||||||
propertyChange.mapValues { it.value.seal() },
|
reset,
|
||||||
childrenChange.mapValues { it.value?.isolate(manager) }
|
vision?.isolate(manager),
|
||||||
|
if (propertyChange.isEmpty()) null else propertyChange.seal(),
|
||||||
|
if (children.isEmpty()) null else children.mapValues { it.value.isolate(manager) }
|
||||||
)
|
)
|
||||||
//TODO optimize isolation for visions without parents?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Vision.isolate(manager: VisionManager): Vision {
|
private fun Vision.isolate(manager: VisionManager): Vision {
|
||||||
@ -46,16 +61,13 @@ private fun Vision.isolate(manager: VisionManager): Vision {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
public data class VisionChange(
|
public class VisionChange(
|
||||||
val propertyChange: Map<Name, @Serializable(MetaSerializer::class) Meta>,
|
public val reset: Boolean = false,
|
||||||
val childrenChange: Map<Name, Vision?>,
|
public val vision: Vision? = null,
|
||||||
|
public val properties: Meta? = null,
|
||||||
|
public val children: Map<Name, VisionChange>? = null,
|
||||||
) {
|
) {
|
||||||
public fun isEmpty(): Boolean = propertyChange.isEmpty() && childrenChange.isEmpty()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A shortcut to the top level property dif
|
|
||||||
*/
|
|
||||||
public val properties: Meta? get() = propertyChange[Name.EMPTY]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public inline fun VisionChange(manager: VisionManager, block: VisionChangeBuilder.() -> Unit): VisionChange =
|
public inline fun VisionChange(manager: VisionManager, block: VisionChangeBuilder.() -> Unit): VisionChange =
|
||||||
@ -69,17 +81,10 @@ private fun CoroutineScope.collectChange(
|
|||||||
) {
|
) {
|
||||||
|
|
||||||
//Collect properties change
|
//Collect properties change
|
||||||
source.config.onChange(this) { propertyName, oldItem, newItem ->
|
source.propertyInvalidated.onEach { propertyName ->
|
||||||
if (oldItem != newItem) {
|
val newItem = source.getProperty(propertyName, inherit = false, includeStyles = false, includeDefaults = false)
|
||||||
launch {
|
collector().propertyChanged(name, propertyName, newItem)
|
||||||
collector().propertyChanged(name, propertyName, newItem)
|
}.launchIn(this)
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
coroutineContext[Job]?.invokeOnCompletion {
|
|
||||||
source.config.removeListener(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (source is VisionGroup) {
|
if (source is VisionGroup) {
|
||||||
//Subscribe for children changes
|
//Subscribe for children changes
|
||||||
@ -89,17 +94,12 @@ private fun CoroutineScope.collectChange(
|
|||||||
|
|
||||||
//Subscribe for structure change
|
//Subscribe for structure change
|
||||||
if (source is MutableVisionGroup) {
|
if (source is MutableVisionGroup) {
|
||||||
source.onStructureChange(this) { token, before, after ->
|
source.structureChanges.onEach { (token, _, after) ->
|
||||||
before?.removeChangeListener(this)
|
|
||||||
(before as? MutableVisionGroup)?.removeStructureChangeListener(this)
|
|
||||||
if (after != null) {
|
if (after != null) {
|
||||||
collectChange(name + token, after, collector)
|
collectChange(name + token, after, collector)
|
||||||
}
|
}
|
||||||
collector()[name + token] = after
|
collector()[name + token] = after
|
||||||
}
|
}.launchIn(this)
|
||||||
coroutineContext[Job]?.invokeOnCompletion {
|
|
||||||
source.removeStructureChangeListener(this)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -111,17 +111,19 @@ public fun Vision.flowChanges(
|
|||||||
): Flow<VisionChange> = flow {
|
): Flow<VisionChange> = flow {
|
||||||
|
|
||||||
var collector = VisionChangeBuilder()
|
var collector = VisionChangeBuilder()
|
||||||
manager.context.collectChange(Name.EMPTY, this@flowChanges) { collector }
|
coroutineScope {
|
||||||
|
collectChange(Name.EMPTY, this@flowChanges) { collector }
|
||||||
|
|
||||||
while (currentCoroutineContext().isActive) {
|
while (currentCoroutineContext().isActive) {
|
||||||
//Wait for changes to accumulate
|
//Wait for changes to accumulate
|
||||||
delay(collectionDuration)
|
delay(collectionDuration)
|
||||||
//Propagate updates only if something is changed
|
//Propagate updates only if something is changed
|
||||||
if (!collector.isEmpty()) {
|
if (!collector.isEmpty()) {
|
||||||
//emit changes
|
//emit changes
|
||||||
emit(collector.isolate(manager))
|
emit(collector.isolate(manager))
|
||||||
//Reset the collector
|
//Reset the collector
|
||||||
collector = VisionChangeBuilder()
|
collector = VisionChangeBuilder()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,6 +2,7 @@ package hep.dataforge.vision
|
|||||||
|
|
||||||
import hep.dataforge.names.*
|
import hep.dataforge.names.*
|
||||||
import hep.dataforge.provider.Provider
|
import hep.dataforge.provider.Provider
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
public interface VisionContainer<out V : Vision> {
|
public interface VisionContainer<out V : Vision> {
|
||||||
public operator fun get(name: Name): V?
|
public operator fun get(name: Name): V?
|
||||||
@ -75,19 +76,12 @@ public interface VisionContainerBuilder<in V : Vision> {
|
|||||||
*/
|
*/
|
||||||
public interface MutableVisionGroup : VisionGroup, VisionContainerBuilder<Vision> {
|
public interface MutableVisionGroup : VisionGroup, VisionContainerBuilder<Vision> {
|
||||||
|
|
||||||
/**
|
public data class StructureChange(val token: NameToken, val before: Vision?, val after: Vision?)
|
||||||
* Add listener for children structure change.
|
|
||||||
* @param owner the handler to properly remove listeners
|
|
||||||
* @param action First argument of the action is the name of changed child. Second argument is the new value of the object.
|
|
||||||
*/
|
|
||||||
public fun onStructureChange(owner: Any?, action: (token: NameToken, before: Vision?, after: Vision?) -> Unit)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove children change listener
|
* Flow structure changes of this group. Unconsumed changes are discarded
|
||||||
*/
|
*/
|
||||||
public fun removeStructureChangeListener(owner: Any?)
|
public val structureChanges: Flow<StructureChange>
|
||||||
|
|
||||||
// public operator fun set(name: Name, child: Vision?)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public operator fun <V : Vision> VisionContainer<V>.get(str: String): V? = get(str.toName())
|
public operator fun <V : Vision> VisionContainer<V>.get(str: String): V? = get(str.toName())
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package hep.dataforge.vision
|
package hep.dataforge.vision
|
||||||
|
|
||||||
import hep.dataforge.meta.configure
|
|
||||||
import hep.dataforge.names.*
|
import hep.dataforge.names.*
|
||||||
|
import kotlinx.coroutines.channels.BufferOverflow
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
import kotlinx.coroutines.flow.SharedFlow
|
||||||
import kotlinx.serialization.SerialName
|
import kotlinx.serialization.SerialName
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.Transient
|
import kotlinx.serialization.Transient
|
||||||
@ -26,54 +28,25 @@ public open class VisionGroupBase : VisionBase(), MutableVisionGroup {
|
|||||||
*/
|
*/
|
||||||
override val children: Map<NameToken, Vision> get() = childrenInternal
|
override val children: Map<NameToken, Vision> get() = childrenInternal
|
||||||
|
|
||||||
override fun propertyChanged(name: Name) {
|
override fun notifyPropertyChanged(propertyName: Name) {
|
||||||
super.propertyChanged(name)
|
super.notifyPropertyChanged(propertyName)
|
||||||
for (obj in this) {
|
for (obj in this) {
|
||||||
obj.propertyChanged(name)
|
obj.notifyPropertyChanged(propertyName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private data class StructureChangeListener(
|
@Transient
|
||||||
val owner: Any?,
|
private val _structureChanges: MutableSharedFlow<MutableVisionGroup.StructureChange> = MutableSharedFlow(
|
||||||
val callback: (token: NameToken, before: Vision?, after: Vision?) -> Unit,
|
onBufferOverflow = BufferOverflow.DROP_OLDEST
|
||||||
)
|
)
|
||||||
|
|
||||||
@Transient
|
override val structureChanges: SharedFlow<MutableVisionGroup.StructureChange> get() = _structureChanges
|
||||||
private val structureChangeListeners = HashSet<StructureChangeListener>()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add listener for children change
|
|
||||||
*/
|
|
||||||
override fun onStructureChange(owner: Any?, action: (token: NameToken, before: Vision?, after: Vision?) -> Unit) {
|
|
||||||
structureChangeListeners.add(
|
|
||||||
StructureChangeListener(
|
|
||||||
owner,
|
|
||||||
action
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove children change listener
|
|
||||||
*/
|
|
||||||
override fun removeStructureChangeListener(owner: Any?) {
|
|
||||||
structureChangeListeners.removeAll { owner == null || it.owner === owner }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Propagate children change event upwards
|
* Propagate children change event upwards
|
||||||
*/
|
*/
|
||||||
protected fun childrenChanged(name: NameToken, before: Vision?, after: Vision?) {
|
private fun childrenChanged(name: NameToken, before: Vision?, after: Vision?) {
|
||||||
structureChangeListeners.forEach { it.callback(name, before, after) }
|
_structureChanges.tryEmit(MutableVisionGroup.StructureChange(name, before, after))
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove a child with given name token
|
|
||||||
*/
|
|
||||||
public fun removeChild(token: NameToken): Vision? {
|
|
||||||
val removed = childrenInternal.remove(token)
|
|
||||||
removed?.parent = null
|
|
||||||
return removed
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -91,12 +64,22 @@ public open class VisionGroupBase : VisionBase(), MutableVisionGroup {
|
|||||||
/**
|
/**
|
||||||
* Set parent for given child and attach it
|
* Set parent for given child and attach it
|
||||||
*/
|
*/
|
||||||
private fun attachChild(token: NameToken, child: Vision) {
|
private fun attachChild(token: NameToken, child: Vision?) {
|
||||||
if (child.parent == null) {
|
val before = children[token]
|
||||||
child.parent = this
|
when {
|
||||||
childrenInternal[token] = child
|
child == null -> {
|
||||||
} else if (child.parent !== this) {
|
childrenInternal.remove(token)
|
||||||
error("Can't reassign existing parent for $child")
|
}
|
||||||
|
child.parent == null -> {
|
||||||
|
child.parent = this
|
||||||
|
childrenInternal[token] = child
|
||||||
|
}
|
||||||
|
child.parent !== this -> {
|
||||||
|
error("Can't reassign existing parent for $child")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (before != child) {
|
||||||
|
childrenChanged(token, before, child)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,13 +116,7 @@ public open class VisionGroupBase : VisionBase(), MutableVisionGroup {
|
|||||||
}
|
}
|
||||||
name.length == 1 -> {
|
name.length == 1 -> {
|
||||||
val token = name.tokens.first()
|
val token = name.tokens.first()
|
||||||
val before = children[token]
|
attachChild(token, child)
|
||||||
if (child == null) {
|
|
||||||
removeChild(token)
|
|
||||||
} else {
|
|
||||||
attachChild(token, child)
|
|
||||||
}
|
|
||||||
childrenChanged(token, before, child)
|
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
//TODO add safety check
|
//TODO add safety check
|
||||||
@ -150,18 +127,12 @@ public open class VisionGroupBase : VisionBase(), MutableVisionGroup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun update(change: VisionChange) {
|
override fun update(change: VisionChange) {
|
||||||
//update stylesheet
|
change.children?.forEach { (name, change) ->
|
||||||
// val changeStyleSheet = change.styleSheet
|
when {
|
||||||
// if (changeStyleSheet != null) {
|
change.reset -> set(name, null)
|
||||||
// styleSheet {
|
change.vision != null -> set(name, change.vision)
|
||||||
// update(changeStyleSheet)
|
else -> get(name)?.update(change)
|
||||||
// }
|
}
|
||||||
// }
|
|
||||||
change.propertyChange.forEach {(childName,configChange)->
|
|
||||||
get(childName)?.configure(configChange)
|
|
||||||
}
|
|
||||||
change.childrenChange.forEach { (name, child) ->
|
|
||||||
set(name, child)
|
|
||||||
}
|
}
|
||||||
super.update(change)
|
super.update(change)
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
package hep.dataforge.vision
|
package hep.dataforge.vision
|
||||||
|
|
||||||
import hep.dataforge.meta.Laminate
|
import hep.dataforge.meta.*
|
||||||
import hep.dataforge.meta.MetaItem
|
|
||||||
import hep.dataforge.meta.descriptors.NodeDescriptor
|
import hep.dataforge.meta.descriptors.NodeDescriptor
|
||||||
import hep.dataforge.meta.node
|
|
||||||
import hep.dataforge.names.Name
|
import hep.dataforge.names.Name
|
||||||
import hep.dataforge.values.ValueType
|
import hep.dataforge.values.ValueType
|
||||||
import hep.dataforge.values.asValue
|
import hep.dataforge.values.asValue
|
||||||
@ -30,4 +28,20 @@ public inline fun <reified E : Enum<E>> NodeDescriptor.enum(key: Name, default:
|
|||||||
default(default)
|
default(default)
|
||||||
}
|
}
|
||||||
allowedValues = enumValues<E>().map { it.asValue() }
|
allowedValues = enumValues<E>().map { it.asValue() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DFExperimental
|
||||||
|
public val Vision.ownProperties: Meta?
|
||||||
|
get() = (this as? VisionBase)?.ownProperties
|
||||||
|
|
||||||
|
@DFExperimental
|
||||||
|
public val Vision.describedProperties: Meta
|
||||||
|
get() = Meta {
|
||||||
|
descriptor?.items?.forEach { (key, _) ->
|
||||||
|
key put getProperty(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun Vision.configure(meta: Meta?): Unit = update(VisionChange(properties = meta))
|
||||||
|
|
||||||
|
public fun Vision.configure(block: MutableMeta<*>.() -> Unit): Unit = configure(Meta(block))
|
@ -1,7 +1,6 @@
|
|||||||
package hep.dataforge.vision.html
|
package hep.dataforge.vision.html
|
||||||
|
|
||||||
import hep.dataforge.meta.DFExperimental
|
import hep.dataforge.meta.DFExperimental
|
||||||
import hep.dataforge.meta.configure
|
|
||||||
import hep.dataforge.meta.set
|
import hep.dataforge.meta.set
|
||||||
import hep.dataforge.vision.VisionBase
|
import hep.dataforge.vision.VisionBase
|
||||||
import kotlinx.html.*
|
import kotlinx.html.*
|
||||||
@ -35,7 +34,7 @@ class HtmlTagTest {
|
|||||||
div {
|
div {
|
||||||
h2 { +"Properties" }
|
h2 { +"Properties" }
|
||||||
ul {
|
ul {
|
||||||
vision.properties?.items?.forEach {
|
(vision as? VisionBase)?.ownProperties?.items?.forEach {
|
||||||
li {
|
li {
|
||||||
a { +it.key.toString() }
|
a { +it.key.toString() }
|
||||||
p { +it.value.toString() }
|
p { +it.value.toString() }
|
||||||
|
@ -1,12 +1,8 @@
|
|||||||
package hep.dataforge.vision.editor
|
package hep.dataforge.vision.editor
|
||||||
|
|
||||||
import hep.dataforge.meta.Config
|
import hep.dataforge.meta.*
|
||||||
import hep.dataforge.meta.Meta
|
|
||||||
import hep.dataforge.meta.descriptors.NodeDescriptor
|
import hep.dataforge.meta.descriptors.NodeDescriptor
|
||||||
import hep.dataforge.meta.update
|
import hep.dataforge.vision.*
|
||||||
import hep.dataforge.vision.Vision
|
|
||||||
import hep.dataforge.vision.getStyle
|
|
||||||
import hep.dataforge.vision.setProperty
|
|
||||||
import javafx.beans.binding.Binding
|
import javafx.beans.binding.Binding
|
||||||
import javafx.beans.property.SimpleObjectProperty
|
import javafx.beans.property.SimpleObjectProperty
|
||||||
import javafx.scene.Node
|
import javafx.scene.Node
|
||||||
@ -23,8 +19,8 @@ class VisualObjectEditorFragment(val selector: (Vision) -> Meta) : Fragment() {
|
|||||||
constructor(
|
constructor(
|
||||||
item: Vision?,
|
item: Vision?,
|
||||||
descriptor: NodeDescriptor?,
|
descriptor: NodeDescriptor?,
|
||||||
selector: (Vision) -> Config = { it.config }
|
selector: (Vision) -> MutableItemProvider = { it.properties }
|
||||||
) : this(selector) {
|
) : this({it.describedProperties}) {
|
||||||
this.item = item
|
this.item = item
|
||||||
this.descriptorProperty.set(descriptor)
|
this.descriptorProperty.set(descriptor)
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ class FX3DPlugin : AbstractPlugin() {
|
|||||||
fun buildNode(obj: Solid): Node {
|
fun buildNode(obj: Solid): Node {
|
||||||
val binding = VisualObjectFXBinding(obj)
|
val binding = VisualObjectFXBinding(obj)
|
||||||
return when (obj) {
|
return when (obj) {
|
||||||
is SolidReference -> referenceFactory(obj, binding)
|
is SolidReferenceGroup -> referenceFactory(obj, binding)
|
||||||
is SolidGroup -> {
|
is SolidGroup -> {
|
||||||
Group(obj.children.mapNotNull { (token, obj) ->
|
Group(obj.children.mapNotNull { (token, obj) ->
|
||||||
(obj as? Solid)?.let {
|
(obj as? Solid)?.let {
|
||||||
|
@ -6,15 +6,15 @@ import javafx.scene.Group
|
|||||||
import javafx.scene.Node
|
import javafx.scene.Node
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
class FXReferenceFactory(val plugin: FX3DPlugin) : FX3DFactory<SolidReference> {
|
class FXReferenceFactory(val plugin: FX3DPlugin) : FX3DFactory<SolidReferenceGroup> {
|
||||||
override val type: KClass<in SolidReference> get() = SolidReference::class
|
override val type: KClass<in SolidReferenceGroup> get() = SolidReferenceGroup::class
|
||||||
|
|
||||||
override fun invoke(obj: SolidReference, binding: VisualObjectFXBinding): Node {
|
override fun invoke(obj: SolidReferenceGroup, binding: VisualObjectFXBinding): Node {
|
||||||
val prototype = obj.prototype
|
val prototype = obj.prototype
|
||||||
val node = plugin.buildNode(prototype)
|
val node = plugin.buildNode(prototype)
|
||||||
|
|
||||||
obj.onPropertyChange(this) { name->
|
obj.onPropertyChange(this) { name->
|
||||||
if (name.firstOrNull()?.body == SolidReference.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()
|
||||||
val referenceChild = obj[childName] ?: error("Reference child with name '$childName' not found")
|
val referenceChild = obj[childName] ?: error("Reference child with name '$childName' not found")
|
||||||
|
@ -2,12 +2,12 @@ package hep.dataforge.vision.gdml
|
|||||||
|
|
||||||
import hep.dataforge.meta.Meta
|
import hep.dataforge.meta.Meta
|
||||||
import hep.dataforge.meta.MetaBuilder
|
import hep.dataforge.meta.MetaBuilder
|
||||||
import hep.dataforge.meta.set
|
|
||||||
import hep.dataforge.names.Name
|
import hep.dataforge.names.Name
|
||||||
import hep.dataforge.names.asName
|
import hep.dataforge.names.asName
|
||||||
import hep.dataforge.names.plus
|
import hep.dataforge.names.plus
|
||||||
import hep.dataforge.names.toName
|
import hep.dataforge.names.toName
|
||||||
import hep.dataforge.vision.set
|
import hep.dataforge.vision.set
|
||||||
|
import hep.dataforge.vision.setProperty
|
||||||
import hep.dataforge.vision.solid.*
|
import hep.dataforge.vision.solid.*
|
||||||
import hep.dataforge.vision.solid.SolidMaterial.Companion.MATERIAL_COLOR_KEY
|
import hep.dataforge.vision.solid.SolidMaterial.Companion.MATERIAL_COLOR_KEY
|
||||||
import hep.dataforge.vision.styleSheet
|
import hep.dataforge.vision.styleSheet
|
||||||
@ -51,13 +51,13 @@ private class GDMLTransformer(val settings: GDMLTransformerSettings) {
|
|||||||
private val proto = SolidGroup()
|
private val proto = SolidGroup()
|
||||||
|
|
||||||
private val solids = proto.group(solidsName) {
|
private val solids = proto.group(solidsName) {
|
||||||
config["edges.enabled"] = false
|
setProperty("edges.enabled", false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private val referenceStore = HashMap<Name, MutableList<SolidReference>>()
|
private val referenceStore = HashMap<Name, MutableList<SolidReferenceGroup>>()
|
||||||
|
|
||||||
private fun proxySolid(root: GDML, group: SolidGroup, solid: GDMLSolid, name: String): SolidReference {
|
private fun proxySolid(root: GDML, group: SolidGroup, solid: GDMLSolid, name: String): SolidReferenceGroup {
|
||||||
val templateName = solidsName + name
|
val templateName = solidsName + name
|
||||||
if (proto[templateName] == null) {
|
if (proto[templateName] == null) {
|
||||||
solids.addSolid(root, solid, name)
|
solids.addSolid(root, solid, name)
|
||||||
@ -67,7 +67,7 @@ private class GDMLTransformer(val settings: GDMLTransformerSettings) {
|
|||||||
return ref
|
return ref
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun proxyVolume(root: GDML, group: SolidGroup, physVolume: GDMLPhysVolume, volume: GDMLGroup): SolidReference {
|
private fun proxyVolume(root: GDML, group: SolidGroup, physVolume: GDMLPhysVolume, volume: GDMLGroup): SolidReferenceGroup {
|
||||||
val templateName = volumesName + volume.name.asName()
|
val templateName = volumesName + volume.name.asName()
|
||||||
if (proto[templateName] == null) {
|
if (proto[templateName] == null) {
|
||||||
proto[templateName] = volume(root, volume)
|
proto[templateName] = volume(root, volume)
|
||||||
|
@ -2,16 +2,10 @@ 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.meta.set
|
import hep.dataforge.vision.Vision
|
||||||
import hep.dataforge.names.Name
|
import hep.dataforge.vision.ownProperties
|
||||||
import hep.dataforge.names.toName
|
import hep.dataforge.vision.properties
|
||||||
import hep.dataforge.vision.*
|
|
||||||
import hep.dataforge.vision.solid.*
|
import hep.dataforge.vision.solid.*
|
||||||
import hep.dataforge.vision.visitor.VisionVisitor
|
|
||||||
import kotlinx.coroutines.CompletableDeferred
|
|
||||||
import kotlinx.coroutines.Job
|
|
||||||
import kotlinx.coroutines.coroutineScope
|
|
||||||
import mu.KotlinLogging
|
|
||||||
|
|
||||||
public expect class Counter() {
|
public expect class Counter() {
|
||||||
public fun get(): Int
|
public fun get(): Int
|
||||||
@ -24,6 +18,7 @@ private fun Point3D?.safePlus(other: Point3D?): Point3D? = if (this == null && o
|
|||||||
(this ?: Point3D(0, 0, 0)) + (other ?: Point3D(0, 0, 0))
|
(this ?: Point3D(0, 0, 0)) + (other ?: Point3D(0, 0, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DFExperimental
|
||||||
internal fun Vision.updateFrom(other: Vision): Vision {
|
internal fun Vision.updateFrom(other: Vision): Vision {
|
||||||
if (this is Solid && other is Solid) {
|
if (this is Solid && other is Solid) {
|
||||||
position = position.safePlus(other.position)
|
position = position.safePlus(other.position)
|
||||||
@ -33,9 +28,9 @@ 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.properties?.sequence()?.forEach { (name, item) ->
|
other.ownProperties?.sequence()?.forEach { (name, item) ->
|
||||||
if (properties?.getItem(name) == null) {
|
if (properties.getItem(name) == null) {
|
||||||
config[name] = item
|
setProperty(name, item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
package hep.dataforge.vision.solid
|
package hep.dataforge.vision.solid
|
||||||
|
|
||||||
import hep.dataforge.meta.update
|
import hep.dataforge.meta.update
|
||||||
@ -18,7 +17,7 @@ public enum class CompositeType {
|
|||||||
public class Composite(
|
public class Composite(
|
||||||
public val compositeType: CompositeType,
|
public val compositeType: CompositeType,
|
||||||
public val first: Solid,
|
public val first: Solid,
|
||||||
public val second: Solid
|
public val second: Solid,
|
||||||
) : SolidBase(), Solid, VisionGroup {
|
) : SolidBase(), Solid, VisionGroup {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@ -34,15 +33,13 @@ public class Composite(
|
|||||||
public inline fun VisionContainerBuilder<Solid>.composite(
|
public inline fun VisionContainerBuilder<Solid>.composite(
|
||||||
type: CompositeType,
|
type: CompositeType,
|
||||||
name: String = "",
|
name: String = "",
|
||||||
builder: SolidGroup.() -> Unit
|
builder: SolidGroup.() -> Unit,
|
||||||
): Composite {
|
): Composite {
|
||||||
val group = SolidGroup().apply(builder)
|
val group = SolidGroup().apply(builder)
|
||||||
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.config.update(group.config)
|
it.configure { update(group.ownProperties) }
|
||||||
//it.material = group.material
|
|
||||||
|
|
||||||
if (group.position != null) {
|
if (group.position != null) {
|
||||||
it.position = group.position
|
it.position = group.position
|
||||||
}
|
}
|
||||||
@ -65,5 +62,8 @@ public inline fun VisionContainerBuilder<Solid>.subtract(name: String = "", buil
|
|||||||
composite(CompositeType.SUBTRACT, name, builder = builder)
|
composite(CompositeType.SUBTRACT, name, builder = builder)
|
||||||
|
|
||||||
@VisionBuilder
|
@VisionBuilder
|
||||||
public inline fun VisionContainerBuilder<Solid>.intersect(name: String = "", builder: SolidGroup.() -> Unit): Composite =
|
public inline fun VisionContainerBuilder<Solid>.intersect(
|
||||||
|
name: String = "",
|
||||||
|
builder: SolidGroup.() -> Unit,
|
||||||
|
): Composite =
|
||||||
composite(CompositeType.INTERSECT, name, builder = builder)
|
composite(CompositeType.INTERSECT, name, builder = builder)
|
@ -1,18 +1,17 @@
|
|||||||
package hep.dataforge.vision.solid
|
package hep.dataforge.vision.solid
|
||||||
|
|
||||||
import hep.dataforge.meta.*
|
import hep.dataforge.meta.boolean
|
||||||
import hep.dataforge.meta.descriptors.NodeDescriptor
|
import hep.dataforge.meta.descriptors.NodeDescriptor
|
||||||
|
import hep.dataforge.meta.enum
|
||||||
|
import hep.dataforge.meta.int
|
||||||
import hep.dataforge.names.Name
|
import hep.dataforge.names.Name
|
||||||
import hep.dataforge.names.asName
|
import hep.dataforge.names.asName
|
||||||
import hep.dataforge.names.plus
|
import hep.dataforge.names.plus
|
||||||
import hep.dataforge.values.ValueType
|
import hep.dataforge.values.ValueType
|
||||||
import hep.dataforge.values.asValue
|
import hep.dataforge.values.asValue
|
||||||
import hep.dataforge.vision.Vision
|
import hep.dataforge.vision.*
|
||||||
import hep.dataforge.vision.Vision.Companion.VISIBLE_KEY
|
import hep.dataforge.vision.Vision.Companion.VISIBLE_KEY
|
||||||
import hep.dataforge.vision.VisionBuilder
|
|
||||||
import hep.dataforge.vision.enum
|
|
||||||
import hep.dataforge.vision.layout.Output
|
import hep.dataforge.vision.layout.Output
|
||||||
import hep.dataforge.vision.setProperty
|
|
||||||
import hep.dataforge.vision.solid.Solid.Companion.DETAIL_KEY
|
import hep.dataforge.vision.solid.Solid.Companion.DETAIL_KEY
|
||||||
import hep.dataforge.vision.solid.Solid.Companion.IGNORE_KEY
|
import hep.dataforge.vision.solid.Solid.Companion.IGNORE_KEY
|
||||||
import hep.dataforge.vision.solid.Solid.Companion.LAYER_KEY
|
import hep.dataforge.vision.solid.Solid.Companion.LAYER_KEY
|
||||||
@ -90,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() ?: 0)
|
result = 31 * result + solid.properties.hashCode()
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -100,9 +99,9 @@ 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() = properties.getItem(LAYER_KEY).int ?: 0
|
||||||
set(value) {
|
set(value) {
|
||||||
config[LAYER_KEY] = value.asValue()
|
setProperty(LAYER_KEY, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisionBuilder
|
@VisionBuilder
|
||||||
@ -153,21 +152,21 @@ public var Solid.x: Number
|
|||||||
get() = position?.x ?: 0f
|
get() = position?.x ?: 0f
|
||||||
set(value) {
|
set(value) {
|
||||||
position().x = value.toDouble()
|
position().x = value.toDouble()
|
||||||
propertyChanged(Solid.X_POSITION_KEY)
|
notifyPropertyChanged(Solid.X_POSITION_KEY)
|
||||||
}
|
}
|
||||||
|
|
||||||
public var Solid.y: Number
|
public var Solid.y: Number
|
||||||
get() = position?.y ?: 0f
|
get() = position?.y ?: 0f
|
||||||
set(value) {
|
set(value) {
|
||||||
position().y = value.toDouble()
|
position().y = value.toDouble()
|
||||||
propertyChanged(Solid.Y_POSITION_KEY)
|
notifyPropertyChanged(Solid.Y_POSITION_KEY)
|
||||||
}
|
}
|
||||||
|
|
||||||
public var Solid.z: Number
|
public var Solid.z: Number
|
||||||
get() = position?.z ?: 0f
|
get() = position?.z ?: 0f
|
||||||
set(value) {
|
set(value) {
|
||||||
position().z = value.toDouble()
|
position().z = value.toDouble()
|
||||||
propertyChanged(Solid.Z_POSITION_KEY)
|
notifyPropertyChanged(Solid.Z_POSITION_KEY)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Solid.rotation(): Point3D =
|
private fun Solid.rotation(): Point3D =
|
||||||
@ -177,21 +176,21 @@ public var Solid.rotationX: Number
|
|||||||
get() = rotation?.x ?: 0f
|
get() = rotation?.x ?: 0f
|
||||||
set(value) {
|
set(value) {
|
||||||
rotation().x = value.toDouble()
|
rotation().x = value.toDouble()
|
||||||
propertyChanged(Solid.X_ROTATION_KEY)
|
notifyPropertyChanged(Solid.X_ROTATION_KEY)
|
||||||
}
|
}
|
||||||
|
|
||||||
public var Solid.rotationY: Number
|
public var Solid.rotationY: Number
|
||||||
get() = rotation?.y ?: 0f
|
get() = rotation?.y ?: 0f
|
||||||
set(value) {
|
set(value) {
|
||||||
rotation().y = value.toDouble()
|
rotation().y = value.toDouble()
|
||||||
propertyChanged(Solid.Y_ROTATION_KEY)
|
notifyPropertyChanged(Solid.Y_ROTATION_KEY)
|
||||||
}
|
}
|
||||||
|
|
||||||
public var Solid.rotationZ: Number
|
public var Solid.rotationZ: Number
|
||||||
get() = rotation?.z ?: 0f
|
get() = rotation?.z ?: 0f
|
||||||
set(value) {
|
set(value) {
|
||||||
rotation().z = value.toDouble()
|
rotation().z = value.toDouble()
|
||||||
propertyChanged(Solid.Z_ROTATION_KEY)
|
notifyPropertyChanged(Solid.Z_ROTATION_KEY)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Solid.scale(): Point3D =
|
private fun Solid.scale(): Point3D =
|
||||||
@ -201,19 +200,19 @@ public var Solid.scaleX: Number
|
|||||||
get() = scale?.x ?: 1f
|
get() = scale?.x ?: 1f
|
||||||
set(value) {
|
set(value) {
|
||||||
scale().x = value.toDouble()
|
scale().x = value.toDouble()
|
||||||
propertyChanged(Solid.X_SCALE_KEY)
|
notifyPropertyChanged(Solid.X_SCALE_KEY)
|
||||||
}
|
}
|
||||||
|
|
||||||
public var Solid.scaleY: Number
|
public var Solid.scaleY: Number
|
||||||
get() = scale?.y ?: 1f
|
get() = scale?.y ?: 1f
|
||||||
set(value) {
|
set(value) {
|
||||||
scale().y = value.toDouble()
|
scale().y = value.toDouble()
|
||||||
propertyChanged(Solid.Y_SCALE_KEY)
|
notifyPropertyChanged(Solid.Y_SCALE_KEY)
|
||||||
}
|
}
|
||||||
|
|
||||||
public var Solid.scaleZ: Number
|
public var Solid.scaleZ: Number
|
||||||
get() = scale?.z ?: 1f
|
get() = scale?.z ?: 1f
|
||||||
set(value) {
|
set(value) {
|
||||||
scale().z = value.toDouble()
|
scale().z = value.toDouble()
|
||||||
propertyChanged(Solid.Z_SCALE_KEY)
|
notifyPropertyChanged(Solid.Z_SCALE_KEY)
|
||||||
}
|
}
|
@ -1,6 +1,6 @@
|
|||||||
package hep.dataforge.vision.solid
|
package hep.dataforge.vision.solid
|
||||||
|
|
||||||
import hep.dataforge.meta.Config
|
import hep.dataforge.meta.MetaItem
|
||||||
import hep.dataforge.meta.descriptors.NodeDescriptor
|
import hep.dataforge.meta.descriptors.NodeDescriptor
|
||||||
import hep.dataforge.names.Name
|
import hep.dataforge.names.Name
|
||||||
import hep.dataforge.names.NameToken
|
import hep.dataforge.names.NameToken
|
||||||
@ -87,7 +87,10 @@ public tailrec fun PrototypeHolder.getPrototype(name: Name): Solid? =
|
|||||||
prototypes?.get(name) as? Solid ?: (parent as? PrototypeHolder)?.getPrototype(name)
|
prototypes?.get(name) as? Solid ?: (parent as? PrototypeHolder)?.getPrototype(name)
|
||||||
|
|
||||||
@VisionBuilder
|
@VisionBuilder
|
||||||
public fun VisionContainerBuilder<Vision>.group(name: Name = Name.EMPTY, action: SolidGroup.() -> Unit = {}): SolidGroup =
|
public fun VisionContainerBuilder<Vision>.group(
|
||||||
|
name: Name = Name.EMPTY,
|
||||||
|
action: SolidGroup.() -> Unit = {},
|
||||||
|
): SolidGroup =
|
||||||
SolidGroup().apply(action).also { set(name, it) }
|
SolidGroup().apply(action).also { set(name, it) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -104,15 +107,10 @@ internal class Prototypes(
|
|||||||
children: Map<NameToken, Vision> = emptyMap(),
|
children: Map<NameToken, Vision> = emptyMap(),
|
||||||
) : VisionGroupBase(), PrototypeHolder {
|
) : VisionGroupBase(), PrototypeHolder {
|
||||||
|
|
||||||
init {
|
override var parent: VisionGroup? = null
|
||||||
this.childrenInternal.putAll(children)
|
|
||||||
}
|
|
||||||
|
|
||||||
override var properties: Config?
|
private val _children = HashMap(children)
|
||||||
get() = null
|
override val children: Map<NameToken, Vision> get() = _children
|
||||||
set(_) {
|
|
||||||
error("Can't define properties for prototypes block")
|
|
||||||
}
|
|
||||||
|
|
||||||
override val prototypes: MutableVisionGroup get() = this
|
override val prototypes: MutableVisionGroup get() = this
|
||||||
|
|
||||||
@ -123,6 +121,21 @@ internal class Prototypes(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getOwnProperty(name: Name): MetaItem<*>? = null
|
||||||
|
|
||||||
|
override fun getProperty(
|
||||||
|
name: Name,
|
||||||
|
inherit: Boolean,
|
||||||
|
includeStyles: Boolean,
|
||||||
|
includeDefaults: Boolean,
|
||||||
|
): MetaItem<*>? = null
|
||||||
|
|
||||||
|
override fun setProperty(name: Name, item: MetaItem<*>?) {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override val descriptor: NodeDescriptor? = null
|
||||||
|
|
||||||
companion object : KSerializer<MutableVisionGroup> {
|
companion object : KSerializer<MutableVisionGroup> {
|
||||||
|
|
||||||
private val mapSerializer: KSerializer<Map<NameToken, Vision>> =
|
private val mapSerializer: KSerializer<Map<NameToken, Vision>> =
|
||||||
|
@ -36,7 +36,7 @@ public class SolidManager(meta: Meta) : AbstractPlugin(meta) {
|
|||||||
|
|
||||||
private fun PolymorphicModuleBuilder<Solid>.solids() {
|
private fun PolymorphicModuleBuilder<Solid>.solids() {
|
||||||
subclass(SolidGroup.serializer())
|
subclass(SolidGroup.serializer())
|
||||||
subclass(SolidReference.serializer())
|
subclass(SolidReferenceGroup.serializer())
|
||||||
subclass(Composite.serializer())
|
subclass(Composite.serializer())
|
||||||
subclass(Tube.serializer())
|
subclass(Tube.serializer())
|
||||||
subclass(Box.serializer())
|
subclass(Box.serializer())
|
||||||
|
@ -10,13 +10,10 @@ import hep.dataforge.values.Value
|
|||||||
import hep.dataforge.values.ValueType
|
import hep.dataforge.values.ValueType
|
||||||
import hep.dataforge.values.asValue
|
import hep.dataforge.values.asValue
|
||||||
import hep.dataforge.values.string
|
import hep.dataforge.values.string
|
||||||
import hep.dataforge.vision.Colors
|
import hep.dataforge.vision.*
|
||||||
import hep.dataforge.vision.VisionBuilder
|
|
||||||
import hep.dataforge.vision.setProperty
|
|
||||||
import hep.dataforge.vision.solid.SolidMaterial.Companion.MATERIAL_COLOR_KEY
|
import hep.dataforge.vision.solid.SolidMaterial.Companion.MATERIAL_COLOR_KEY
|
||||||
import hep.dataforge.vision.solid.SolidMaterial.Companion.MATERIAL_KEY
|
import hep.dataforge.vision.solid.SolidMaterial.Companion.MATERIAL_KEY
|
||||||
import hep.dataforge.vision.solid.SolidMaterial.Companion.MATERIAL_OPACITY_KEY
|
import hep.dataforge.vision.solid.SolidMaterial.Companion.MATERIAL_OPACITY_KEY
|
||||||
import hep.dataforge.vision.widgetType
|
|
||||||
|
|
||||||
@VisionBuilder
|
@VisionBuilder
|
||||||
public class ColorAccessor(private val parent: MutableItemProvider, private val colorKey: Name) {
|
public class ColorAccessor(private val parent: MutableItemProvider, private val colorKey: Name) {
|
||||||
@ -115,7 +112,7 @@ public class SolidMaterial : Scheme() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public val Solid.color: ColorAccessor get() = ColorAccessor(config, MATERIAL_COLOR_KEY)
|
public val Solid.color: ColorAccessor get() = ColorAccessor(properties, 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) }
|
||||||
@ -123,11 +120,11 @@ public var Solid.material: SolidMaterial?
|
|||||||
|
|
||||||
@VisionBuilder
|
@VisionBuilder
|
||||||
public fun Solid.material(builder: SolidMaterial.() -> Unit) {
|
public fun Solid.material(builder: SolidMaterial.() -> Unit) {
|
||||||
val node = config[MATERIAL_KEY].node
|
val node = properties.getItem(MATERIAL_KEY).node
|
||||||
if (node != null) {
|
if (node != null) {
|
||||||
SolidMaterial.update(node, builder)
|
SolidMaterial.update(node, builder)
|
||||||
} else {
|
} else {
|
||||||
config[MATERIAL_KEY] = SolidMaterial(builder)
|
setProperty(MATERIAL_KEY, SolidMaterial(builder))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,156 +0,0 @@
|
|||||||
package hep.dataforge.vision.solid
|
|
||||||
|
|
||||||
import hep.dataforge.meta.*
|
|
||||||
import hep.dataforge.meta.descriptors.NodeDescriptor
|
|
||||||
import hep.dataforge.names.*
|
|
||||||
import hep.dataforge.vision.*
|
|
||||||
import kotlinx.serialization.SerialName
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
import kotlinx.serialization.Transient
|
|
||||||
import kotlin.collections.set
|
|
||||||
|
|
||||||
public abstract class AbstractReference : SolidBase(), VisionGroup {
|
|
||||||
public abstract val prototype: Solid
|
|
||||||
|
|
||||||
override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? = sequence {
|
|
||||||
yield(properties?.get(name))
|
|
||||||
yieldAll(getStyleItems(name))
|
|
||||||
yield(prototype.getProperty(name))
|
|
||||||
if (inherit) {
|
|
||||||
yield(parent?.getProperty(name, inherit))
|
|
||||||
}
|
|
||||||
}.merge()
|
|
||||||
|
|
||||||
override var styles: List<String>
|
|
||||||
get() = (properties[Vision.STYLE_KEY]?.stringList ?: emptyList()) + prototype.styles
|
|
||||||
set(value) {
|
|
||||||
config[Vision.STYLE_KEY] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
override val allProperties: Laminate
|
|
||||||
get() = Laminate(
|
|
||||||
properties,
|
|
||||||
allStyles,
|
|
||||||
prototype.allProperties,
|
|
||||||
parent?.allProperties,
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun attachChildren() {
|
|
||||||
//do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
override val descriptor: NodeDescriptor get() = prototype.descriptor
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A reference [Solid] to reuse a template object
|
|
||||||
*/
|
|
||||||
@Serializable
|
|
||||||
@SerialName("solid.ref")
|
|
||||||
public class SolidReference(
|
|
||||||
public val templateName: Name,
|
|
||||||
) : AbstractReference(), Solid {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Recursively search for defined template in the parent
|
|
||||||
*/
|
|
||||||
override val prototype: Solid
|
|
||||||
get() = (parent as? SolidGroup)?.getPrototype(templateName)
|
|
||||||
?: error("Prototype with name $templateName not found in $parent")
|
|
||||||
|
|
||||||
@Transient
|
|
||||||
private val propertyCache: HashMap<Name, Config> = HashMap()
|
|
||||||
|
|
||||||
|
|
||||||
override val children: Map<NameToken, SolidReference.ReferenceChild>
|
|
||||||
get() = (prototype as? VisionGroup)?.children
|
|
||||||
?.filter { !it.key.toString().startsWith("@") }
|
|
||||||
?.mapValues {
|
|
||||||
ReferenceChild(it.key.asName())
|
|
||||||
} ?: emptyMap()
|
|
||||||
|
|
||||||
private fun childPropertyName(childName: Name, propertyName: Name): Name {
|
|
||||||
return NameToken(REFERENCE_CHILD_PROPERTY_PREFIX, childName.toString()) + propertyName
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun prototypeFor(name: Name): Solid {
|
|
||||||
return (prototype as? SolidGroup)?.get(name) as? Solid
|
|
||||||
?: error("Prototype with name $name not found in $this")
|
|
||||||
}
|
|
||||||
|
|
||||||
//override fun findAllStyles(): Laminate = Laminate((styles + prototype.styles).mapNotNull { findStyle(it) })
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A ProxyChild is created temporarily only to interact with properties, it does not store any values
|
|
||||||
* (properties are stored in external cache) and created and destroyed on-demand).
|
|
||||||
*/
|
|
||||||
public inner class ReferenceChild(public val name: Name) : AbstractReference() {
|
|
||||||
|
|
||||||
override val prototype: Solid get() = prototypeFor(name)
|
|
||||||
|
|
||||||
override val children: Map<NameToken, Vision>
|
|
||||||
get() = (prototype as? VisionGroup)?.children
|
|
||||||
?.filter { !it.key.toString().startsWith("@") }
|
|
||||||
?.mapValues { (key, _) ->
|
|
||||||
ReferenceChild(name + key.asName())
|
|
||||||
} ?: emptyMap()
|
|
||||||
|
|
||||||
override var properties: Config?
|
|
||||||
get() = propertyCache[name]
|
|
||||||
set(value) {
|
|
||||||
if (value == null) {
|
|
||||||
propertyCache.remove(name)?.also {
|
|
||||||
//Removing listener if it is present
|
|
||||||
removeChangeListener(this@SolidReference)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
propertyCache[name] = value.also {
|
|
||||||
onPropertyChange(this@SolidReference) { propertyName ->
|
|
||||||
this@SolidReference.propertyChanged(childPropertyName(name, propertyName))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public companion object {
|
|
||||||
public const val REFERENCE_CHILD_PROPERTY_PREFIX: String = "@child"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a vision prototype if it is a [SolidReference] or vision itself if it is not
|
|
||||||
*/
|
|
||||||
public val Vision.prototype: Vision
|
|
||||||
get() = when (this) {
|
|
||||||
is AbstractReference -> prototype
|
|
||||||
else -> this
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create ref for existing prototype
|
|
||||||
*/
|
|
||||||
public fun SolidGroup.ref(
|
|
||||||
templateName: Name,
|
|
||||||
name: String = "",
|
|
||||||
): SolidReference = SolidReference(templateName).also { set(name, it) }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add new [SolidReference] wrapping given object and automatically adding it to the prototypes
|
|
||||||
*/
|
|
||||||
public fun SolidGroup.ref(
|
|
||||||
name: String,
|
|
||||||
obj: Solid,
|
|
||||||
templateName: Name = name.toName(),
|
|
||||||
): SolidReference {
|
|
||||||
val existing = getPrototype(templateName)
|
|
||||||
if (existing == null) {
|
|
||||||
prototypes {
|
|
||||||
this[templateName] = obj
|
|
||||||
}
|
|
||||||
} else if (existing != obj) {
|
|
||||||
error("Can't add different prototype on top of existing one")
|
|
||||||
}
|
|
||||||
return this@ref.ref(templateName, name)
|
|
||||||
}
|
|
@ -0,0 +1,189 @@
|
|||||||
|
package hep.dataforge.vision.solid
|
||||||
|
|
||||||
|
import hep.dataforge.meta.*
|
||||||
|
import hep.dataforge.meta.descriptors.NodeDescriptor
|
||||||
|
import hep.dataforge.names.*
|
||||||
|
import hep.dataforge.vision.*
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.filter
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
public interface SolidReference : Vision {
|
||||||
|
public val prototype: Solid
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A reference [Solid] to reuse a template object
|
||||||
|
*/
|
||||||
|
@Serializable
|
||||||
|
@SerialName("solid.ref")
|
||||||
|
public class SolidReferenceGroup(
|
||||||
|
public val templateName: Name,
|
||||||
|
) : SolidBase(), SolidReference, VisionGroup {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively search for defined template in the parent
|
||||||
|
*/
|
||||||
|
override val prototype: Solid
|
||||||
|
get() = (parent as? SolidGroup)?.getPrototype(templateName)
|
||||||
|
?: error("Prototype with name $templateName not found in $parent")
|
||||||
|
|
||||||
|
override val children: Map<NameToken, Vision>
|
||||||
|
get() = (prototype as? VisionGroup)?.children
|
||||||
|
?.filter { !it.key.toString().startsWith("@") }
|
||||||
|
?.mapValues {
|
||||||
|
ReferenceChild(it.key.asName())
|
||||||
|
} ?: emptyMap()
|
||||||
|
|
||||||
|
private fun childToken(childName: Name): NameToken =
|
||||||
|
NameToken(REFERENCE_CHILD_PROPERTY_PREFIX, childName.toString())
|
||||||
|
|
||||||
|
private fun childPropertyName(childName: Name, propertyName: Name): Name =
|
||||||
|
childToken(childName) + propertyName
|
||||||
|
|
||||||
|
private fun getChildProperty(childName: Name, propertyName: Name): MetaItem<*>? {
|
||||||
|
return getOwnProperty(childPropertyName(childName, propertyName))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setChildProperty(childName: Name, propertyName: Name, item: MetaItem<*>?) {
|
||||||
|
setProperty(childPropertyName(childName, propertyName), item)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun prototypeFor(name: Name): Solid {
|
||||||
|
return if(name.isEmpty()) prototype else {
|
||||||
|
(prototype as? SolidGroup)?.get(name) as? Solid
|
||||||
|
?: error("Prototype with name $name not found in $this")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getProperty(
|
||||||
|
name: Name,
|
||||||
|
inherit: Boolean,
|
||||||
|
includeStyles: Boolean,
|
||||||
|
includeDefaults: Boolean,
|
||||||
|
): MetaItem<*>? = sequence {
|
||||||
|
yield(getOwnProperty(name))
|
||||||
|
if (includeStyles) {
|
||||||
|
yieldAll(getStyleItems(name))
|
||||||
|
}
|
||||||
|
yield(prototype.getProperty(name, inherit, includeStyles, includeDefaults))
|
||||||
|
if (inherit) {
|
||||||
|
yield(parent?.getProperty(name, inherit))
|
||||||
|
}
|
||||||
|
}.merge()
|
||||||
|
|
||||||
|
override fun attachChildren() {
|
||||||
|
//do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
override val descriptor: NodeDescriptor get() = prototype.descriptor
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A ProxyChild is created temporarily only to interact with properties, it does not store any values
|
||||||
|
* (properties are stored in external cache) and created and destroyed on-demand).
|
||||||
|
*/
|
||||||
|
private inner class ReferenceChild(private val childName: Name) : SolidReference, VisionGroup {
|
||||||
|
|
||||||
|
override val prototype: Solid get() = prototypeFor(childName)
|
||||||
|
|
||||||
|
override val children: Map<NameToken, Vision>
|
||||||
|
get() = (prototype as? VisionGroup)?.children
|
||||||
|
?.filter { !it.key.toString().startsWith("@") }
|
||||||
|
?.mapValues { (key, _) ->
|
||||||
|
ReferenceChild(childName + key.asName())
|
||||||
|
} ?: emptyMap()
|
||||||
|
|
||||||
|
override fun getOwnProperty(name: Name): MetaItem<*>? = getChildProperty(childName, name)
|
||||||
|
|
||||||
|
override fun setProperty(name: Name, item: MetaItem<*>?) {
|
||||||
|
setChildProperty(childName, name, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getProperty(
|
||||||
|
name: Name,
|
||||||
|
inherit: Boolean,
|
||||||
|
includeStyles: Boolean,
|
||||||
|
includeDefaults: Boolean,
|
||||||
|
): MetaItem<*>? = sequence {
|
||||||
|
yield(getOwnProperty(name))
|
||||||
|
if (includeStyles) {
|
||||||
|
yieldAll(getStyleItems(name))
|
||||||
|
}
|
||||||
|
yield(prototype.getProperty(name, inherit, includeStyles, includeDefaults))
|
||||||
|
if (inherit) {
|
||||||
|
yield(parent?.getProperty(name, inherit))
|
||||||
|
}
|
||||||
|
}.merge()
|
||||||
|
|
||||||
|
override var parent: VisionGroup?
|
||||||
|
get(){
|
||||||
|
val parentName = childName.cutLast()
|
||||||
|
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 ->
|
||||||
|
name.startsWith(childToken(childName))
|
||||||
|
}.map { name ->
|
||||||
|
name.cutFirst()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun notifyPropertyChanged(propertyName: Name) {
|
||||||
|
this@SolidReferenceGroup.notifyPropertyChanged(childPropertyName(childName, propertyName))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun update(change: VisionChange) {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun attachChildren() {
|
||||||
|
//do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
override val descriptor: NodeDescriptor get() = prototype.descriptor
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public companion object {
|
||||||
|
public const val REFERENCE_CHILD_PROPERTY_PREFIX: String = "@child"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a vision prototype if it is a [SolidReferenceGroup] or vision itself if it is not
|
||||||
|
*/
|
||||||
|
public val Vision.prototype: Vision
|
||||||
|
get() = if (this is SolidReference) prototype else this
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create ref for existing prototype
|
||||||
|
*/
|
||||||
|
public fun SolidGroup.ref(
|
||||||
|
templateName: Name,
|
||||||
|
name: String = "",
|
||||||
|
): SolidReferenceGroup = SolidReferenceGroup(templateName).also { set(name, it) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add new [SolidReferenceGroup] wrapping given object and automatically adding it to the prototypes
|
||||||
|
*/
|
||||||
|
public fun SolidGroup.ref(
|
||||||
|
name: String,
|
||||||
|
obj: Solid,
|
||||||
|
templateName: Name = name.toName(),
|
||||||
|
): SolidReferenceGroup {
|
||||||
|
val existing = getPrototype(templateName)
|
||||||
|
if (existing == null) {
|
||||||
|
prototypes {
|
||||||
|
this[templateName] = obj
|
||||||
|
}
|
||||||
|
} else if (existing != obj) {
|
||||||
|
error("Can't add different prototype on top of existing one")
|
||||||
|
}
|
||||||
|
return this@ref.ref(templateName, name)
|
||||||
|
}
|
@ -1,18 +1,15 @@
|
|||||||
package hep.dataforge.vision.solid.transform
|
package hep.dataforge.vision.solid.transform
|
||||||
|
|
||||||
import hep.dataforge.meta.DFExperimental
|
import hep.dataforge.meta.DFExperimental
|
||||||
import hep.dataforge.meta.update
|
|
||||||
import hep.dataforge.names.asName
|
import hep.dataforge.names.asName
|
||||||
import hep.dataforge.vision.MutableVisionGroup
|
import hep.dataforge.vision.*
|
||||||
import hep.dataforge.vision.Vision
|
|
||||||
import hep.dataforge.vision.VisionGroup
|
|
||||||
import hep.dataforge.vision.solid.*
|
import hep.dataforge.vision.solid.*
|
||||||
|
|
||||||
@DFExperimental
|
@DFExperimental
|
||||||
internal fun mergeChild(parent: VisionGroup, child: Vision): Vision {
|
internal fun mergeChild(parent: VisionGroup, child: Vision): Vision {
|
||||||
return child.apply {
|
return child.apply {
|
||||||
|
|
||||||
config.update(parent.config)
|
configure(parent.ownProperties)
|
||||||
|
|
||||||
//parent.properties?.let { config.update(it) }
|
//parent.properties?.let { config.update(it) }
|
||||||
|
|
||||||
@ -40,7 +37,7 @@ internal object RemoveSingleChild : VisualTreeTransform<SolidGroup>() {
|
|||||||
override fun SolidGroup.transformInPlace() {
|
override fun SolidGroup.transformInPlace() {
|
||||||
fun MutableVisionGroup.replaceChildren() {
|
fun MutableVisionGroup.replaceChildren() {
|
||||||
children.forEach { (childName, parent) ->
|
children.forEach { (childName, parent) ->
|
||||||
if (parent is SolidReference) return@forEach //ignore refs
|
if (parent is SolidReferenceGroup) return@forEach //ignore refs
|
||||||
if (parent is MutableVisionGroup) {
|
if (parent is MutableVisionGroup) {
|
||||||
parent.replaceChildren()
|
parent.replaceChildren()
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ import hep.dataforge.names.asName
|
|||||||
import hep.dataforge.vision.MutableVisionGroup
|
import hep.dataforge.vision.MutableVisionGroup
|
||||||
import hep.dataforge.vision.VisionGroup
|
import hep.dataforge.vision.VisionGroup
|
||||||
import hep.dataforge.vision.solid.SolidGroup
|
import hep.dataforge.vision.solid.SolidGroup
|
||||||
import hep.dataforge.vision.solid.SolidReference
|
import hep.dataforge.vision.solid.SolidReferenceGroup
|
||||||
|
|
||||||
@DFExperimental
|
@DFExperimental
|
||||||
internal object UnRef : VisualTreeTransform<SolidGroup>() {
|
internal object UnRef : VisualTreeTransform<SolidGroup>() {
|
||||||
@ -17,7 +17,7 @@ internal object UnRef : VisualTreeTransform<SolidGroup>() {
|
|||||||
counter.forEach { (key, value) ->
|
counter.forEach { (key, value) ->
|
||||||
reducer[key] = (reducer[key] ?: 0) + value
|
reducer[key] = (reducer[key] ?: 0) + value
|
||||||
}
|
}
|
||||||
} else if (obj is SolidReference) {
|
} else if (obj is SolidReferenceGroup) {
|
||||||
reducer[obj.templateName] = (reducer[obj.templateName] ?: 0) + 1
|
reducer[obj.templateName] = (reducer[obj.templateName] ?: 0) + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,8 +27,8 @@ internal object UnRef : VisualTreeTransform<SolidGroup>() {
|
|||||||
|
|
||||||
private fun MutableVisionGroup.unref(name: Name) {
|
private fun MutableVisionGroup.unref(name: Name) {
|
||||||
(this as? SolidGroup)?.prototypes?.set(name, null)
|
(this as? SolidGroup)?.prototypes?.set(name, null)
|
||||||
children.filter { (it.value as? SolidReference)?.templateName == name }.forEach { (key, value) ->
|
children.filter { (it.value as? SolidReferenceGroup)?.templateName == name }.forEach { (key, value) ->
|
||||||
val reference = value as SolidReference
|
val reference = value as SolidReferenceGroup
|
||||||
val newChild = mergeChild(reference, reference.prototype)
|
val newChild = mergeChild(reference, reference.prototype)
|
||||||
newChild.parent = null
|
newChild.parent = null
|
||||||
set(key.asName(), newChild) // replace proxy with merged object
|
set(key.asName(), newChild) // replace proxy with merged object
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package hep.dataforge.vision.solid
|
package hep.dataforge.vision.solid
|
||||||
|
|
||||||
import hep.dataforge.meta.int
|
import hep.dataforge.meta.int
|
||||||
import hep.dataforge.meta.set
|
|
||||||
import hep.dataforge.names.asName
|
import hep.dataforge.names.asName
|
||||||
|
import hep.dataforge.vision.setProperty
|
||||||
import hep.dataforge.vision.styleSheet
|
import hep.dataforge.vision.styleSheet
|
||||||
import hep.dataforge.vision.useStyle
|
import hep.dataforge.vision.useStyle
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
@ -14,7 +14,7 @@ class PropertyTest {
|
|||||||
fun testInheritedProperty() {
|
fun testInheritedProperty() {
|
||||||
var box: Box? = null
|
var box: Box? = null
|
||||||
val group = SolidGroup().apply {
|
val group = SolidGroup().apply {
|
||||||
config["test"] = 22
|
setProperty("test", 22)
|
||||||
group {
|
group {
|
||||||
box = box(100, 100, 100)
|
box = box(100, 100, 100)
|
||||||
}
|
}
|
||||||
@ -60,7 +60,7 @@ class PropertyTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testReferenceStyleProperty() {
|
fun testReferenceStyleProperty() {
|
||||||
var box: SolidReference? = null
|
var box: SolidReferenceGroup? = null
|
||||||
val group = SolidGroup{
|
val group = SolidGroup{
|
||||||
styleSheet {
|
styleSheet {
|
||||||
set("testStyle") {
|
set("testStyle") {
|
||||||
|
@ -4,6 +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 kotlin.test.Test
|
import kotlin.test.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
@ -15,7 +16,7 @@ fun SolidGroup.refGroup(
|
|||||||
name: String,
|
name: String,
|
||||||
templateName: Name = name.toName(),
|
templateName: Name = name.toName(),
|
||||||
block: MutableVisionGroup.() -> Unit
|
block: MutableVisionGroup.() -> Unit
|
||||||
): SolidReference {
|
): SolidReferenceGroup {
|
||||||
val group = SolidGroup().apply(block)
|
val group = SolidGroup().apply(block)
|
||||||
return ref(name, group, templateName)
|
return ref(name, group, templateName)
|
||||||
}
|
}
|
||||||
@ -32,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.config, newCube.config)
|
assertEquals(cube.ownProperties, newCube.ownProperties)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -53,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"]?.config, reconstructed["cube"]?.config)
|
assertEquals(group["cube"]?.ownProperties, reconstructed["cube"]?.ownProperties)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -45,7 +45,7 @@ class VisionUpdateTest {
|
|||||||
val serialized = visionManager.jsonFormat.encodeToString(VisionChange.serializer(), change)
|
val serialized = visionManager.jsonFormat.encodeToString(VisionChange.serializer(), change)
|
||||||
println(serialized)
|
println(serialized)
|
||||||
val reconstructed = visionManager.jsonFormat.decodeFromString(VisionChange.serializer(), serialized)
|
val reconstructed = visionManager.jsonFormat.decodeFromString(VisionChange.serializer(), serialized)
|
||||||
assertEquals(change.propertyChange,reconstructed.propertyChange)
|
assertEquals(change.properties,reconstructed.properties)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -15,20 +15,22 @@ import info.laht.threekt.geometries.EdgesGeometry
|
|||||||
import info.laht.threekt.geometries.WireframeGeometry
|
import info.laht.threekt.geometries.WireframeGeometry
|
||||||
import info.laht.threekt.objects.LineSegments
|
import info.laht.threekt.objects.LineSegments
|
||||||
import info.laht.threekt.objects.Mesh
|
import info.laht.threekt.objects.Mesh
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Basic geometry-based factory
|
* Basic geometry-based factory
|
||||||
*/
|
*/
|
||||||
public abstract class MeshThreeFactory<in T : Solid>(
|
public abstract class MeshThreeFactory<in T : Solid>(
|
||||||
override val type: KClass<in T>
|
override val type: KClass<in T>,
|
||||||
) : ThreeFactory<T> {
|
) : ThreeFactory<T> {
|
||||||
/**
|
/**
|
||||||
* Build a geometry for an object
|
* Build a geometry for an object
|
||||||
*/
|
*/
|
||||||
public abstract fun buildGeometry(obj: T): BufferGeometry
|
public abstract fun buildGeometry(obj: T): BufferGeometry
|
||||||
|
|
||||||
override fun invoke(obj: T): Mesh {
|
override fun invoke(three: ThreePlugin, obj: T): Mesh {
|
||||||
val geometry = buildGeometry(obj)
|
val geometry = buildGeometry(obj)
|
||||||
|
|
||||||
//JS sometimes tries to pass Geometry as BufferGeometry
|
//JS sometimes tries to pass Geometry as BufferGeometry
|
||||||
@ -36,14 +38,14 @@ public abstract class MeshThreeFactory<in T : Solid>(
|
|||||||
|
|
||||||
//val meshMeta: Meta = obj.properties[Material3D.MATERIAL_KEY]?.node ?: Meta.empty
|
//val meshMeta: Meta = obj.properties[Material3D.MATERIAL_KEY]?.node ?: Meta.empty
|
||||||
|
|
||||||
val mesh = Mesh(geometry, null).apply{
|
val mesh = Mesh(geometry, null).apply {
|
||||||
matrixAutoUpdate = false
|
matrixAutoUpdate = false
|
||||||
//set position for mesh
|
//set position for mesh
|
||||||
updatePosition(obj)
|
updatePosition(obj)
|
||||||
}.applyProperties(obj)
|
}.applyProperties(obj)
|
||||||
|
|
||||||
//add listener to object properties
|
//add listener to object properties
|
||||||
obj.onPropertyChange(this) { name ->
|
obj.propertyInvalidated.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
|
||||||
@ -57,7 +59,8 @@ public abstract class MeshThreeFactory<in T : Solid>(
|
|||||||
name.startsWith(EDGES_KEY) -> mesh.applyEdges(obj)
|
name.startsWith(EDGES_KEY) -> mesh.applyEdges(obj)
|
||||||
else -> mesh.updateProperty(obj, name)
|
else -> mesh.updateProperty(obj, name)
|
||||||
}
|
}
|
||||||
}
|
}.launchIn(three.updateScope)
|
||||||
|
|
||||||
return mesh
|
return mesh
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,7 +75,7 @@ public abstract class MeshThreeFactory<in T : Solid>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Mesh.applyProperties(obj: Solid): Mesh = apply{
|
internal fun Mesh.applyProperties(obj: Solid): Mesh = apply {
|
||||||
material = getMaterial(obj, true)
|
material = getMaterial(obj, true)
|
||||||
applyEdges(obj)
|
applyEdges(obj)
|
||||||
applyWireFrame(obj)
|
applyWireFrame(obj)
|
||||||
@ -82,7 +85,7 @@ fun Mesh.applyProperties(obj: Solid): Mesh = apply{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Mesh.applyEdges(obj: Solid) {
|
internal fun Mesh.applyEdges(obj: Solid) {
|
||||||
val edges = children.find { it.name == "@edges" } as? LineSegments
|
val edges = children.find { it.name == "@edges" } as? LineSegments
|
||||||
//inherited edges definition, enabled by default
|
//inherited edges definition, enabled by default
|
||||||
if (obj.getProperty(MeshThreeFactory.EDGES_ENABLED_KEY).boolean != false) {
|
if (obj.getProperty(MeshThreeFactory.EDGES_ENABLED_KEY).boolean != false) {
|
||||||
@ -108,7 +111,7 @@ fun Mesh.applyEdges(obj: Solid) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Mesh.applyWireFrame(obj: Solid) {
|
internal fun Mesh.applyWireFrame(obj: Solid) {
|
||||||
children.find { it.name == "@wireframe" }?.let {
|
children.find { it.name == "@wireframe" }?.let {
|
||||||
remove(it)
|
remove(it)
|
||||||
(it as LineSegments).dispose()
|
(it as LineSegments).dispose()
|
||||||
@ -116,7 +119,8 @@ fun Mesh.applyWireFrame(obj: Solid) {
|
|||||||
//inherited wireframe definition, disabled by default
|
//inherited wireframe definition, disabled by default
|
||||||
if (obj.getProperty(MeshThreeFactory.WIREFRAME_ENABLED_KEY).boolean == true) {
|
if (obj.getProperty(MeshThreeFactory.WIREFRAME_ENABLED_KEY).boolean == true) {
|
||||||
val bufferGeometry = geometry as? BufferGeometry ?: return
|
val bufferGeometry = geometry as? BufferGeometry ?: return
|
||||||
val material = ThreeMaterials.getLineMaterial(obj.getProperty(MeshThreeFactory.WIREFRAME_MATERIAL_KEY).node, true)
|
val material =
|
||||||
|
ThreeMaterials.getLineMaterial(obj.getProperty(MeshThreeFactory.WIREFRAME_MATERIAL_KEY).node, true)
|
||||||
add(
|
add(
|
||||||
LineSegments(
|
LineSegments(
|
||||||
WireframeGeometry(bufferGeometry),
|
WireframeGeometry(bufferGeometry),
|
||||||
|
@ -2,11 +2,10 @@ package hep.dataforge.vision.solid.three
|
|||||||
|
|
||||||
import hep.dataforge.vision.solid.Convex
|
import hep.dataforge.vision.solid.Convex
|
||||||
import info.laht.threekt.external.geometries.ConvexBufferGeometry
|
import info.laht.threekt.external.geometries.ConvexBufferGeometry
|
||||||
import info.laht.threekt.math.Vector3
|
|
||||||
|
|
||||||
public object ThreeConvexFactory : MeshThreeFactory<Convex>(Convex::class) {
|
public object ThreeConvexFactory : MeshThreeFactory<Convex>(Convex::class) {
|
||||||
override fun buildGeometry(obj: Convex): ConvexBufferGeometry {
|
override fun buildGeometry(obj: Convex): ConvexBufferGeometry {
|
||||||
@Suppress("USELESS_CAST") val vectors = obj.points.toTypedArray() as Array<Vector3>
|
val vectors = obj.points.map { it.toVector() }.toTypedArray()
|
||||||
return ConvexBufferGeometry(vectors)
|
return ConvexBufferGeometry(vectors)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -22,7 +22,7 @@ public interface ThreeFactory<in T : Vision> {
|
|||||||
|
|
||||||
public val type: KClass<in T>
|
public val type: KClass<in T>
|
||||||
|
|
||||||
public operator fun invoke(obj: T): Object3D
|
public operator fun invoke(three: ThreePlugin, obj: T): Object3D
|
||||||
|
|
||||||
public companion object {
|
public companion object {
|
||||||
public const val TYPE: String = "threeFactory"
|
public const val TYPE: String = "threeFactory"
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
package hep.dataforge.vision.solid.three
|
package hep.dataforge.vision.solid.three
|
||||||
|
|
||||||
|
|
||||||
|
import hep.dataforge.context.logger
|
||||||
import hep.dataforge.vision.solid.SolidLabel
|
import hep.dataforge.vision.solid.SolidLabel
|
||||||
import hep.dataforge.vision.solid.three.ThreeMaterials.getMaterial
|
import hep.dataforge.vision.solid.three.ThreeMaterials.getMaterial
|
||||||
import info.laht.threekt.core.Object3D
|
import info.laht.threekt.core.Object3D
|
||||||
import info.laht.threekt.geometries.TextBufferGeometry
|
import info.laht.threekt.geometries.TextBufferGeometry
|
||||||
import info.laht.threekt.objects.Mesh
|
import info.laht.threekt.objects.Mesh
|
||||||
import kotlinext.js.jsObject
|
import kotlinext.js.jsObject
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -15,18 +18,19 @@ import kotlin.reflect.KClass
|
|||||||
public object ThreeLabelFactory : ThreeFactory<SolidLabel> {
|
public object ThreeLabelFactory : ThreeFactory<SolidLabel> {
|
||||||
override val type: KClass<in SolidLabel> get() = SolidLabel::class
|
override val type: KClass<in SolidLabel> get() = SolidLabel::class
|
||||||
|
|
||||||
override fun invoke(obj: SolidLabel): Object3D {
|
override fun invoke(three: ThreePlugin, obj: SolidLabel): Object3D {
|
||||||
val textGeo = TextBufferGeometry(obj.text, jsObject {
|
val textGeo = TextBufferGeometry(obj.text, jsObject {
|
||||||
font = obj.fontFamily
|
font = obj.fontFamily
|
||||||
size = 20
|
size = 20
|
||||||
height = 1
|
height = 1
|
||||||
curveSegments = 1
|
curveSegments = 1
|
||||||
})
|
})
|
||||||
return Mesh(textGeo, getMaterial(obj,true)).apply {
|
return Mesh(textGeo, getMaterial(obj, true)).apply {
|
||||||
updatePosition(obj)
|
updatePosition(obj)
|
||||||
obj.onPropertyChange(this@ThreeLabelFactory) { _ ->
|
obj.propertyInvalidated.onEach { _ ->
|
||||||
//TODO
|
//TODO
|
||||||
}
|
three.logger.warn{"Label parameter change not implemented"}
|
||||||
|
}.launchIn(three.updateScope)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -9,12 +9,14 @@ import info.laht.threekt.core.Geometry
|
|||||||
import info.laht.threekt.core.Object3D
|
import info.laht.threekt.core.Object3D
|
||||||
import info.laht.threekt.math.Color
|
import info.laht.threekt.math.Color
|
||||||
import info.laht.threekt.objects.LineSegments
|
import info.laht.threekt.objects.LineSegments
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
public object ThreeLineFactory : ThreeFactory<PolyLine> {
|
public object ThreeLineFactory : ThreeFactory<PolyLine> {
|
||||||
override val type: KClass<PolyLine> get() = PolyLine::class
|
override val type: KClass<PolyLine> get() = PolyLine::class
|
||||||
|
|
||||||
override fun invoke(obj: PolyLine): Object3D {
|
override fun invoke(three: ThreePlugin, obj: PolyLine): Object3D {
|
||||||
val geometry = Geometry().apply {
|
val geometry = Geometry().apply {
|
||||||
vertices = Array(obj.points.size) { obj.points[it].toVector() }
|
vertices = Array(obj.points.size) { obj.points[it].toVector() }
|
||||||
}
|
}
|
||||||
@ -28,9 +30,9 @@ 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.onPropertyChange(this) { propertyName ->
|
obj.propertyInvalidated.onEach { propertyName ->
|
||||||
updateProperty(obj, propertyName)
|
updateProperty(obj, propertyName)
|
||||||
}
|
}.launchIn(three.updateScope)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,9 @@ import hep.dataforge.vision.solid.*
|
|||||||
import hep.dataforge.vision.solid.specifications.Canvas3DOptions
|
import hep.dataforge.vision.solid.specifications.Canvas3DOptions
|
||||||
import hep.dataforge.vision.visible
|
import hep.dataforge.vision.visible
|
||||||
import info.laht.threekt.core.Object3D
|
import info.laht.threekt.core.Object3D
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
import org.w3c.dom.Element
|
import org.w3c.dom.Element
|
||||||
import org.w3c.dom.HTMLElement
|
import org.w3c.dom.HTMLElement
|
||||||
import kotlin.collections.set
|
import kotlin.collections.set
|
||||||
@ -24,6 +27,9 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
|
|||||||
private val compositeFactory = ThreeCompositeFactory(this)
|
private val compositeFactory = ThreeCompositeFactory(this)
|
||||||
private val refFactory = ThreeReferenceFactory(this)
|
private val refFactory = ThreeReferenceFactory(this)
|
||||||
|
|
||||||
|
//TODO generate a separate supervisor update scope
|
||||||
|
internal val updateScope: CoroutineScope get() = context
|
||||||
|
|
||||||
init {
|
init {
|
||||||
//Add specialized factories here
|
//Add specialized factories here
|
||||||
objectFactories[Box::class] = ThreeBoxFactory
|
objectFactories[Box::class] = ThreeBoxFactory
|
||||||
@ -44,7 +50,7 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
|
|||||||
public fun buildObject3D(obj: Solid): Object3D {
|
public fun buildObject3D(obj: Solid): Object3D {
|
||||||
return when (obj) {
|
return when (obj) {
|
||||||
is ThreeVision -> obj.render()
|
is ThreeVision -> obj.render()
|
||||||
is SolidReference -> refFactory(obj)
|
is SolidReferenceGroup -> refFactory(obj)
|
||||||
is SolidGroup -> {
|
is SolidGroup -> {
|
||||||
val group = ThreeGroup()
|
val group = ThreeGroup()
|
||||||
obj.children.forEach { (token, child) ->
|
obj.children.forEach { (token, child) ->
|
||||||
@ -63,7 +69,7 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
|
|||||||
updatePosition(obj)
|
updatePosition(obj)
|
||||||
//obj.onChildrenChange()
|
//obj.onChildrenChange()
|
||||||
|
|
||||||
obj.onPropertyChange(this) { name ->
|
obj.propertyInvalidated.onEach { name ->
|
||||||
if (
|
if (
|
||||||
name.startsWith(Solid.POSITION_KEY) ||
|
name.startsWith(Solid.POSITION_KEY) ||
|
||||||
name.startsWith(Solid.ROTATION) ||
|
name.startsWith(Solid.ROTATION) ||
|
||||||
@ -74,9 +80,9 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
|
|||||||
} else if (name == Vision.VISIBLE_KEY) {
|
} else if (name == Vision.VISIBLE_KEY) {
|
||||||
visible = obj.visible ?: true
|
visible = obj.visible ?: true
|
||||||
}
|
}
|
||||||
}
|
}.launchIn(updateScope)
|
||||||
|
|
||||||
obj.onStructureChange(this) { nameToken, _, child ->
|
obj.structureChanges.onEach { (nameToken, _, child) ->
|
||||||
// if (name.isEmpty()) {
|
// if (name.isEmpty()) {
|
||||||
// logger.error { "Children change with empty name on $group" }
|
// logger.error { "Children change with empty name on $group" }
|
||||||
// return@onChildrenChange
|
// return@onChildrenChange
|
||||||
@ -99,7 +105,7 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
|
|||||||
logger.error(ex) { "Failed to render $child" }
|
logger.error(ex) { "Failed to render $child" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}.launchIn(updateScope)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is Composite -> compositeFactory(obj)
|
is Composite -> compositeFactory(obj)
|
||||||
|
@ -4,17 +4,19 @@ import hep.dataforge.names.cutFirst
|
|||||||
import hep.dataforge.names.firstOrNull
|
import hep.dataforge.names.firstOrNull
|
||||||
import hep.dataforge.names.toName
|
import hep.dataforge.names.toName
|
||||||
import hep.dataforge.vision.solid.Solid
|
import hep.dataforge.vision.solid.Solid
|
||||||
import hep.dataforge.vision.solid.SolidReference
|
import hep.dataforge.vision.solid.SolidReferenceGroup
|
||||||
import hep.dataforge.vision.solid.SolidReference.Companion.REFERENCE_CHILD_PROPERTY_PREFIX
|
import hep.dataforge.vision.solid.SolidReferenceGroup.Companion.REFERENCE_CHILD_PROPERTY_PREFIX
|
||||||
import info.laht.threekt.core.BufferGeometry
|
import info.laht.threekt.core.BufferGeometry
|
||||||
import info.laht.threekt.core.Object3D
|
import info.laht.threekt.core.Object3D
|
||||||
import info.laht.threekt.objects.Mesh
|
import info.laht.threekt.objects.Mesh
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
public class ThreeReferenceFactory(public val three: ThreePlugin) : ThreeFactory<SolidReference> {
|
public class ThreeReferenceFactory(public val three: ThreePlugin) : ThreeFactory<SolidReferenceGroup> {
|
||||||
private val cache = HashMap<Solid, Object3D>()
|
private val cache = HashMap<Solid, Object3D>()
|
||||||
|
|
||||||
override val type: KClass<SolidReference> = SolidReference::class
|
override val type: KClass<SolidReferenceGroup> = SolidReferenceGroup::class
|
||||||
|
|
||||||
private fun Object3D.replicate(): Object3D {
|
private fun Object3D.replicate(): Object3D {
|
||||||
return when (this) {
|
return when (this) {
|
||||||
@ -30,7 +32,7 @@ public class ThreeReferenceFactory(public val three: ThreePlugin) : ThreeFactory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun invoke(obj: SolidReference): Object3D {
|
override fun invoke(obj: SolidReferenceGroup): Object3D {
|
||||||
val template = obj.prototype
|
val template = obj.prototype
|
||||||
val cachedObject = cache.getOrPut(template) {
|
val cachedObject = cache.getOrPut(template) {
|
||||||
three.buildObject3D(template)
|
three.buildObject3D(template)
|
||||||
@ -43,7 +45,9 @@ public class ThreeReferenceFactory(public val three: ThreePlugin) : ThreeFactory
|
|||||||
object3D.applyProperties(obj)
|
object3D.applyProperties(obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
obj.onPropertyChange(this) { name ->
|
//TODO apply child properties
|
||||||
|
|
||||||
|
obj.propertyInvalidated.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()
|
||||||
@ -53,7 +57,8 @@ public class ThreeReferenceFactory(public val three: ThreePlugin) : ThreeFactory
|
|||||||
} else {
|
} else {
|
||||||
object3D.updateProperty(obj, name)
|
object3D.updateProperty(obj, name)
|
||||||
}
|
}
|
||||||
}
|
}.launchIn(three.updateScope)
|
||||||
|
|
||||||
|
|
||||||
return object3D
|
return object3D
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user