forked from kscience/visionforge
Change collector update
This commit is contained in:
parent
fdc221dfa1
commit
dfac01d352
@ -3,8 +3,10 @@
|
||||
## [Unreleased]
|
||||
### Added
|
||||
- Server module
|
||||
- Change collector
|
||||
|
||||
### Changed
|
||||
- Vision does not implement ItemProvider anymore. Property changes are done via `getProperty`/`setProperty` and `property` delegate.
|
||||
|
||||
### Deprecated
|
||||
|
||||
|
@ -4,8 +4,8 @@ plugins {
|
||||
id("ru.mipt.npm.project")
|
||||
}
|
||||
|
||||
val dataforgeVersion by extra("0.2.0-dev-4")
|
||||
val ktorVersion by extra("1.4.1")
|
||||
val dataforgeVersion by extra("0.2.0-dev-7")
|
||||
val ktorVersion by extra("1.4.2")
|
||||
val kotlinWrappersVersion by extra("pre.129-kotlin-1.4.10")
|
||||
|
||||
allprojects {
|
||||
|
@ -1,8 +1,5 @@
|
||||
package hep.dataforge.vision
|
||||
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.get
|
||||
import hep.dataforge.meta.node
|
||||
import hep.dataforge.names.*
|
||||
import kotlinx.serialization.Transient
|
||||
|
||||
@ -10,7 +7,7 @@ import kotlinx.serialization.Transient
|
||||
/**
|
||||
* Abstract implementation of mutable group of [Vision]
|
||||
*/
|
||||
public abstract class AbstractVisionGroup : AbstractVision(), MutableVisionGroup {
|
||||
public abstract class AbstractVisionGroup : VisionBase(), MutableVisionGroup {
|
||||
|
||||
//protected abstract val _children: MutableMap<NameToken, T>
|
||||
|
||||
@ -19,13 +16,13 @@ public abstract class AbstractVisionGroup : AbstractVision(), MutableVisionGroup
|
||||
*/
|
||||
abstract override val children: Map<NameToken, Vision>
|
||||
|
||||
abstract override var styleSheet: StyleSheet?
|
||||
protected set
|
||||
final override var styleSheet: StyleSheet? = null
|
||||
private set
|
||||
|
||||
/**
|
||||
* Update or create stylesheet
|
||||
*/
|
||||
public fun styleSheet(block: StyleSheet.() -> Unit) {
|
||||
public open fun styleSheet(block: StyleSheet.() -> Unit) {
|
||||
if (styleSheet == null) {
|
||||
styleSheet = StyleSheet(this@AbstractVisionGroup)
|
||||
}
|
||||
@ -84,17 +81,18 @@ public abstract class AbstractVisionGroup : AbstractVision(), MutableVisionGroup
|
||||
* Add a static child. Statics could not be found by name, removed or replaced. Changing statics also do not trigger events.
|
||||
*/
|
||||
protected open fun addStatic(child: Vision): Unit {
|
||||
setChild(NameToken("@static", index = child.hashCode().toString()), child)
|
||||
attach(NameToken("@static", index = child.hashCode().toString()), child)
|
||||
}
|
||||
|
||||
protected abstract fun createGroup(): AbstractVisionGroup
|
||||
|
||||
/**
|
||||
* Set this node as parent for given node
|
||||
* Set parent for given child and attach it
|
||||
*/
|
||||
protected fun attach(child: Vision) {
|
||||
protected fun attach(token: NameToken, child: Vision) {
|
||||
if (child.parent == null) {
|
||||
child.parent = this
|
||||
setChild(token, child)
|
||||
} else if (child.parent !== this) {
|
||||
error("Can't reassign existing parent for $child")
|
||||
}
|
||||
@ -110,8 +108,7 @@ public abstract class AbstractVisionGroup : AbstractVision(), MutableVisionGroup
|
||||
val token = name.tokens.first()
|
||||
when (val current = children[token]) {
|
||||
null -> createGroup().also { child ->
|
||||
attach(child)
|
||||
setChild(token, child)
|
||||
attach(token, child)
|
||||
}
|
||||
is AbstractVisionGroup -> current
|
||||
else -> error("Can't create group with name $name because it exists and not a group")
|
||||
@ -129,7 +126,6 @@ public abstract class AbstractVisionGroup : AbstractVision(), MutableVisionGroup
|
||||
when {
|
||||
name.isEmpty() -> {
|
||||
if (child != null) {
|
||||
attach(child)
|
||||
addStatic(child)
|
||||
}
|
||||
}
|
||||
@ -138,8 +134,7 @@ public abstract class AbstractVisionGroup : AbstractVision(), MutableVisionGroup
|
||||
if (child == null) {
|
||||
removeChild(token)
|
||||
} else {
|
||||
attach(child)
|
||||
setChild(token, child)
|
||||
attach(token, child)
|
||||
}
|
||||
childrenChanged(token, child)
|
||||
}
|
||||
@ -151,11 +146,23 @@ public abstract class AbstractVisionGroup : AbstractVision(), MutableVisionGroup
|
||||
}
|
||||
}
|
||||
|
||||
override fun update(meta: Meta) {
|
||||
val styleMeta = meta.get("styleSheet").node
|
||||
if (styleMeta != null) {
|
||||
this.styleSheet?.update(styleMeta)
|
||||
override fun update(change: Vision) {
|
||||
if (change is VisionGroup) {
|
||||
//update stylesheet
|
||||
val changeStyleSheet = change.styleSheet
|
||||
if (changeStyleSheet != null) {
|
||||
styleSheet {
|
||||
update(changeStyleSheet)
|
||||
}
|
||||
}
|
||||
change.children.forEach { (token, child) ->
|
||||
when {
|
||||
child is NullVision -> removeChild(token)
|
||||
children.containsKey(token) -> children[token]!!.update(child)
|
||||
else -> attach(token, child)
|
||||
}
|
||||
}
|
||||
}
|
||||
super.update(meta)
|
||||
super.update(change)
|
||||
}
|
||||
}
|
@ -1,17 +1,13 @@
|
||||
package hep.dataforge.vision
|
||||
|
||||
import hep.dataforge.meta.Config
|
||||
import hep.dataforge.names.NameToken
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
|
||||
@Serializable
|
||||
@SerialName("group")
|
||||
@SerialName("vision.group")
|
||||
public class SimpleVisionGroup : AbstractVisionGroup() {
|
||||
|
||||
override var styleSheet: StyleSheet? = null
|
||||
|
||||
@SerialName("children")
|
||||
private val _children = HashMap<NameToken, Vision>()
|
||||
override val children: Map<NameToken, Vision> get() = _children
|
||||
|
@ -5,7 +5,10 @@ package hep.dataforge.vision
|
||||
import hep.dataforge.meta.*
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.asName
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
import kotlinx.serialization.UseSerializers
|
||||
import kotlinx.serialization.builtins.MapSerializer
|
||||
import kotlinx.serialization.builtins.serializer
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
@ -15,7 +18,7 @@ import kotlinx.serialization.encoding.Encoder
|
||||
/**
|
||||
* A container for styles
|
||||
*/
|
||||
@Serializable
|
||||
@Serializable(StyleSheet.Companion::class)
|
||||
public class StyleSheet private constructor(private val styleMap: MutableMap<String, Meta>) {
|
||||
@Transient
|
||||
internal var owner: Vision? = null
|
||||
@ -30,8 +33,9 @@ public class StyleSheet private constructor(private val styleMap: MutableMap<Str
|
||||
private fun Vision.styleChanged(key: String, oldStyle: Meta?, newStyle: Meta?) {
|
||||
if (styles.contains(key)) {
|
||||
//TODO optimize set concatenation
|
||||
val tokens: Collection<Name> = ((oldStyle?.items?.keys ?: emptySet()) + (newStyle?.items?.keys ?: emptySet()))
|
||||
.map { it.asName() }
|
||||
val tokens: Collection<Name> =
|
||||
((oldStyle?.items?.keys ?: emptySet()) + (newStyle?.items?.keys ?: emptySet()))
|
||||
.map { it.asName() }
|
||||
tokens.forEach { parent?.propertyChanged(it) }
|
||||
}
|
||||
if (this is VisionGroup) {
|
||||
@ -73,8 +77,17 @@ public class StyleSheet private constructor(private val styleMap: MutableMap<Str
|
||||
set(key, newStyle.seal())
|
||||
}
|
||||
|
||||
@ExperimentalSerializationApi
|
||||
@Serializer(StyleSheet::class)
|
||||
public fun update(key: String, meta: Meta) {
|
||||
val existing = get(key)
|
||||
set(key, existing?.edit { this.update(meta) } ?: meta)
|
||||
}
|
||||
|
||||
public fun update(other: StyleSheet) {
|
||||
other.items.forEach { (key, value) ->
|
||||
update(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
public companion object : KSerializer<StyleSheet> {
|
||||
private val mapSerializer = MapSerializer(String.serializer(), MetaSerializer)
|
||||
override val descriptor: SerialDescriptor get() = mapSerializer.descriptor
|
||||
@ -91,11 +104,6 @@ public class StyleSheet private constructor(private val styleMap: MutableMap<Str
|
||||
}
|
||||
}
|
||||
|
||||
public fun StyleSheet.update(styleMeta: Meta){
|
||||
TODO()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add style name to the list of styles to be resolved later. The style with given name does not necessary exist at the moment.
|
||||
*/
|
||||
|
@ -1,6 +1,7 @@
|
||||
package hep.dataforge.vision
|
||||
|
||||
import hep.dataforge.meta.*
|
||||
import hep.dataforge.meta.descriptors.Described
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.asName
|
||||
import hep.dataforge.names.toName
|
||||
@ -9,14 +10,15 @@ import hep.dataforge.values.asValue
|
||||
import hep.dataforge.vision.Vision.Companion.TYPE
|
||||
import hep.dataforge.vision.Vision.Companion.VISIBLE_KEY
|
||||
import kotlinx.serialization.PolymorphicSerializer
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
import kotlin.properties.ReadWriteProperty
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
/**
|
||||
* A root type for display hierarchy
|
||||
*/
|
||||
@Type(TYPE)
|
||||
public interface Vision : Configurable {
|
||||
public interface Vision : Configurable, Described {
|
||||
|
||||
/**
|
||||
* The parent object of this one. If null, this one is a root.
|
||||
@ -37,12 +39,7 @@ public interface Vision : Configurable {
|
||||
/**
|
||||
* Get property (including styles). [inherit] toggles parent node property lookup
|
||||
*/
|
||||
public fun getProperty(name: Name, inherit: Boolean): MetaItem<*>?
|
||||
|
||||
/**
|
||||
* Ger a property including inherited values
|
||||
*/
|
||||
override fun getItem(name: Name): MetaItem<*>? = getProperty(name, true)
|
||||
public fun getProperty(name: Name, inherit: Boolean = true): MetaItem<*>?
|
||||
|
||||
/**
|
||||
* Trigger property invalidation event. If [name] is empty, notify that the whole object is changed
|
||||
@ -63,7 +60,7 @@ public interface Vision : Configurable {
|
||||
* List of names of styles applied to this object. Order matters. Not inherited.
|
||||
*/
|
||||
public var styles: List<String>
|
||||
get() = properties[STYLE_KEY]?.stringList?: emptyList()
|
||||
get() = properties[STYLE_KEY]?.stringList ?: emptyList()
|
||||
set(value) {
|
||||
config[STYLE_KEY] = value
|
||||
}
|
||||
@ -71,7 +68,7 @@ public interface Vision : Configurable {
|
||||
/**
|
||||
* Update this vision using external meta. Children are not updated.
|
||||
*/
|
||||
public fun update(meta: Meta)
|
||||
public fun update(change: Vision)
|
||||
|
||||
public companion object {
|
||||
public const val TYPE: String = "vision"
|
||||
@ -89,16 +86,61 @@ public interface Vision : Configurable {
|
||||
public fun Vision.getProperty(key: String, inherit: Boolean = true): MetaItem<*>? =
|
||||
getProperty(key.toName(), inherit)
|
||||
|
||||
/**
|
||||
* A convenience method to pair [getProperty]
|
||||
*/
|
||||
public fun Vision.setProperty(key: Name, value: Any?) {
|
||||
config[key] = value
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience method to pair [getProperty]
|
||||
*/
|
||||
public fun Vision.setProperty(key: String, value: Any?) {
|
||||
config[key] = value
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a style with given name for given [Vision]. The style is not necessary applied to this [Vision].
|
||||
*/
|
||||
public tailrec fun Vision.resolveStyle(name: String): Meta? =
|
||||
(this as? VisionGroup)?.styleSheet?.get(name) ?: parent?.resolveStyle(name)
|
||||
|
||||
|
||||
/**
|
||||
* Control visibility of the element
|
||||
*/
|
||||
public var Vision.visible: Boolean?
|
||||
get() = getItem(VISIBLE_KEY).boolean
|
||||
set(value) = setItem(VISIBLE_KEY, value?.asValue())
|
||||
get() = getProperty(VISIBLE_KEY).boolean
|
||||
set(value) = config.setValue(VISIBLE_KEY, value?.asValue())
|
||||
|
||||
/**
|
||||
* Convinience 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)
|
||||
}
|
||||
}
|
||||
|
||||
//TODO replace by value
|
||||
fun Vision.properties(inherit: Boolean = true): MutableItemProvider = object : MutableItemProvider {
|
||||
override fun getItem(name: Name): MetaItem<*>? {
|
||||
return getProperty(name, inherit)
|
||||
}
|
||||
|
||||
override fun setItem(name: Name, item: MetaItem<*>?) {
|
||||
setProperty(name, item)
|
||||
}
|
||||
|
||||
}
|
@ -2,20 +2,24 @@ package hep.dataforge.vision
|
||||
|
||||
import hep.dataforge.meta.*
|
||||
import hep.dataforge.meta.descriptors.NodeDescriptor
|
||||
import hep.dataforge.meta.descriptors.defaultItem
|
||||
import hep.dataforge.meta.descriptors.get
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.asName
|
||||
import hep.dataforge.values.ValueType
|
||||
import hep.dataforge.vision.Vision.Companion.STYLE_KEY
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
|
||||
internal data class PropertyListener(
|
||||
val owner: Any? = null,
|
||||
val action: (name: Name) -> Unit
|
||||
val action: (name: Name) -> Unit,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
public open class AbstractVision : Vision {
|
||||
@SerialName("vision")
|
||||
public open class VisionBase : Vision {
|
||||
|
||||
@Transient
|
||||
override var parent: VisionGroup? = null
|
||||
@ -26,6 +30,8 @@ public open class AbstractVision : Vision {
|
||||
override var properties: Config? = null
|
||||
protected set
|
||||
|
||||
override val descriptor: NodeDescriptor? get() = null
|
||||
|
||||
protected fun updateStyles(names: List<String>) {
|
||||
names.mapNotNull { resolveStyle(it) }.asSequence()
|
||||
.flatMap { it.items.asSequence() }
|
||||
@ -76,12 +82,14 @@ public open class AbstractVision : Vision {
|
||||
sequence {
|
||||
yield(properties?.get(name))
|
||||
yieldAll(getStyleItems(name))
|
||||
yield(descriptor?.get(name)?.defaultItem())
|
||||
yield(parent?.getProperty(name, inherit))
|
||||
}.merge()
|
||||
} else {
|
||||
sequence {
|
||||
yield(properties?.get(name))
|
||||
yieldAll(getStyleItems(name))
|
||||
yield(descriptor?.get(name)?.defaultItem())
|
||||
}.merge()
|
||||
}
|
||||
}
|
||||
@ -94,8 +102,10 @@ public open class AbstractVision : Vision {
|
||||
properties = null
|
||||
}
|
||||
|
||||
override fun update(meta: Meta) {
|
||||
meta[Vision::properties.name].node?.let { configure(it) }
|
||||
override fun update(change: Vision) {
|
||||
if (change.properties != null) {
|
||||
config.update(change.config)
|
||||
}
|
||||
}
|
||||
|
||||
public companion object {
|
@ -1,50 +0,0 @@
|
||||
package hep.dataforge.vision
|
||||
|
||||
import hep.dataforge.meta.Config
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.get
|
||||
import hep.dataforge.meta.set
|
||||
import hep.dataforge.names.NameToken
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
|
||||
|
||||
//public class VisionChange(public val properties: Meta, public val childrenChange: Map<NameToken, VisionChange>)
|
||||
|
||||
public class VisionChangeCollector(
|
||||
public val manager: VisionManager,
|
||||
public val scope: CoroutineScope,
|
||||
public val vision: Vision,
|
||||
public val lock: Mutex = Mutex()
|
||||
) {
|
||||
private val collector: Config = Config()
|
||||
private val childrenCollectors = HashMap<NameToken, VisionChangeCollector>()
|
||||
|
||||
init {
|
||||
vision.onPropertyChange(this) { propertyName ->
|
||||
collector[propertyName] = vision.properties?.get(propertyName)
|
||||
}
|
||||
if (vision is VisionGroup) {
|
||||
vision.children.forEach { (token, child) ->
|
||||
childrenCollectors[token] = VisionChangeCollector(manager, scope, child, lock)
|
||||
}
|
||||
}
|
||||
if (vision is MutableVisionGroup) {
|
||||
TODO("Tread vision structure change")
|
||||
// vision.onChildrenChange(this) { childName, child ->
|
||||
// if(child == null){
|
||||
// childrenCollectors[childName] = null
|
||||
// } else {
|
||||
// childrenCollectors[childName] = manager.encodeToMeta(child)
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
//fun collectUpdates(manager: VisionManager, scope: CoroutineScope, vision: Vision): Flow<Meta> {
|
||||
//
|
||||
//
|
||||
// vision.
|
||||
//}
|
@ -3,10 +3,6 @@ package hep.dataforge.vision
|
||||
import hep.dataforge.context.*
|
||||
import hep.dataforge.meta.*
|
||||
import hep.dataforge.meta.descriptors.NodeDescriptor
|
||||
import hep.dataforge.names.asName
|
||||
import hep.dataforge.values.Null
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.modules.SerializersModule
|
||||
@ -52,30 +48,30 @@ public class VisionManager(meta: Meta) : AbstractPlugin(meta) {
|
||||
encodeToJsonElement(vision).toMetaItem(descriptor).node
|
||||
?: error("Expected node, but value found. Check your serializer!")
|
||||
|
||||
public fun updateVision(vision: Vision, meta: Meta) {
|
||||
vision.update(meta)
|
||||
if (vision is MutableVisionGroup) {
|
||||
val children by meta.node()
|
||||
children?.items?.forEach { (token, item) ->
|
||||
when {
|
||||
item.value == Null -> vision[token] = null //Null means removal
|
||||
item.node != null -> {
|
||||
val node = item.node!!
|
||||
val type by node.string()
|
||||
if (type != null) {
|
||||
//If the type is present considering it as new node, not an update
|
||||
vision[token.asName()] = decodeFromMeta(node)
|
||||
} else {
|
||||
val existing = vision.children[token]
|
||||
if (existing != null) {
|
||||
updateVision(existing, node)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// public fun updateVision(vision: Vision, meta: Meta) {
|
||||
// vision.update(meta)
|
||||
// if (vision is MutableVisionGroup) {
|
||||
// val children by meta.node()
|
||||
// children?.items?.forEach { (token, item) ->
|
||||
// when {
|
||||
// item.value == Null -> vision[token] = null //Null means removal
|
||||
// item.node != null -> {
|
||||
// val node = item.node!!
|
||||
// val type by node.string()
|
||||
// if (type != null) {
|
||||
// //If the type is present considering it as new node, not an update
|
||||
// vision[token.asName()] = decodeFromMeta(node)
|
||||
// } else {
|
||||
// val existing = vision.children[token]
|
||||
// if (existing != null) {
|
||||
// updateVision(existing, node)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
public companion object : PluginFactory<VisionManager> {
|
||||
override val tag: PluginTag = PluginTag(name = "vision", group = PluginTag.DATAFORGE_GROUP)
|
||||
|
@ -0,0 +1,106 @@
|
||||
package hep.dataforge.vision
|
||||
|
||||
import hep.dataforge.meta.*
|
||||
import hep.dataforge.meta.descriptors.ItemDescriptor
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.NameToken
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlin.time.Duration
|
||||
|
||||
/**
|
||||
* An empty vision existing only for Vision tree change representation. [NullVision] should not be used outside update logic.
|
||||
*/
|
||||
@Serializable
|
||||
@SerialName("vision.null")
|
||||
public object NullVision : Vision {
|
||||
|
||||
@Suppress("SetterBackingFieldAssignment")
|
||||
override var parent: VisionGroup? = null
|
||||
set(value) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
override val properties: Config? = null
|
||||
|
||||
override fun getAllProperties(): Laminate = Laminate(Meta.EMPTY)
|
||||
|
||||
override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? = null
|
||||
|
||||
override fun propertyChanged(name: Name) {}
|
||||
|
||||
override fun onPropertyChange(owner: Any?, action: (Name) -> Unit) {}
|
||||
|
||||
override fun removeChangeListener(owner: Any?) {}
|
||||
|
||||
override fun update(change: Vision) {
|
||||
error("Null vision should be removed, not updated")
|
||||
}
|
||||
|
||||
override val config: Config get() = Config()
|
||||
override val descriptor: ItemDescriptor? get() = null
|
||||
}
|
||||
|
||||
private fun Vision.collectChange(scope: CoroutineScope, collector: Vision): Job = scope.launch {
|
||||
//Store job to be able to cancel collection jobs
|
||||
//TODO add lock for concurrent modification protection?
|
||||
val jobStore = HashMap<NameToken, Job>()
|
||||
|
||||
if (this is VisionGroup) {
|
||||
check(collector is MutableVisionGroup) { "Collector for a group should be a group" }
|
||||
//Subscribe for children changes
|
||||
children.forEach { (token, child) ->
|
||||
val childCollector: Vision = if (child is VisionGroup) {
|
||||
SimpleVisionGroup()
|
||||
} else {
|
||||
VisionBase()
|
||||
}
|
||||
val job = child.collectChange(this, childCollector)
|
||||
jobStore[token] = job
|
||||
//TODO add lazy child addition
|
||||
collector[token] = childCollector
|
||||
}
|
||||
|
||||
//Subscribe for structure change
|
||||
if (this is MutableVisionGroup) {
|
||||
onChildrenChange(this) { token, child ->
|
||||
//Cancel collector job to avoid leaking
|
||||
jobStore[token]?.cancel()
|
||||
if (child != null) {
|
||||
//Collect to existing Vision
|
||||
val job = child.collectChange(this, child)
|
||||
jobStore[token] = job
|
||||
collector[token] = child
|
||||
} else{
|
||||
collector[token] = NullVision
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Collect properties change
|
||||
collector.onPropertyChange(collector) { propertyName ->
|
||||
collector.config[propertyName] = properties?.get(propertyName)
|
||||
}
|
||||
}
|
||||
|
||||
public fun Vision.flowChanges(scope: CoroutineScope, collectionDuration: Duration): Flow<Vision> = flow {
|
||||
//emit initial visual tree
|
||||
emit(this@flowChanges)
|
||||
while (true) {
|
||||
val collector: Vision = if (this is VisionGroup) {
|
||||
SimpleVisionGroup()
|
||||
} else {
|
||||
VisionBase()
|
||||
}
|
||||
val collectorJob = collectChange(scope, collector)
|
||||
kotlinx.coroutines.delay(collectionDuration)
|
||||
emit(collector)
|
||||
collectorJob.cancel()
|
||||
}
|
||||
}
|
@ -1,63 +1,8 @@
|
||||
//import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.Companion.MAIN_COMPILATION_NAME
|
||||
|
||||
plugins {
|
||||
id("ru.mipt.npm.mpp")
|
||||
// application
|
||||
id("ru.mipt.npm.jvm")
|
||||
}
|
||||
|
||||
val ktorVersion: String by rootProject.extra
|
||||
|
||||
//kscience {
|
||||
// application()
|
||||
//}
|
||||
|
||||
kotlin {
|
||||
// afterEvaluate {
|
||||
// val jsBrowserDistribution by tasks.getting
|
||||
//
|
||||
// jvm {
|
||||
// withJava()
|
||||
// compilations[MAIN_COMPILATION_NAME]?.apply {
|
||||
// tasks.getByName<ProcessResources>(processResourcesTaskName) {
|
||||
// dependsOn(jsBrowserDistribution)
|
||||
// afterEvaluate {
|
||||
// from(jsBrowserDistribution)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// }
|
||||
// }
|
||||
|
||||
sourceSets {
|
||||
commonMain {
|
||||
dependencies {
|
||||
implementation(project(":visionforge-core"))
|
||||
}
|
||||
}
|
||||
jvmMain {
|
||||
dependencies {
|
||||
implementation("io.ktor:ktor-server-cio:$ktorVersion")
|
||||
implementation("io.ktor:ktor-serialization:$ktorVersion")
|
||||
}
|
||||
}
|
||||
jsMain {
|
||||
dependencies {
|
||||
implementation("io.ktor:ktor-client-js:$ktorVersion")
|
||||
implementation("io.ktor:ktor-client-serialization-js:$ktorVersion")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//distributions {
|
||||
// main {
|
||||
// contents {
|
||||
// from("$buildDir/libs") {
|
||||
// rename("${rootProject.name}-jvm", rootProject.name)
|
||||
// into("lib")
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
@ -1,15 +1,20 @@
|
||||
package hep.dataforge.vision.solid
|
||||
|
||||
import hep.dataforge.meta.*
|
||||
import hep.dataforge.vision.AbstractVision
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.descriptors.NodeDescriptor
|
||||
import hep.dataforge.meta.float
|
||||
import hep.dataforge.meta.get
|
||||
import hep.dataforge.meta.node
|
||||
import hep.dataforge.vision.Vision
|
||||
import hep.dataforge.vision.VisionBase
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.UseSerializers
|
||||
|
||||
@Serializable
|
||||
@SerialName("solid")
|
||||
public open class BasicSolid: AbstractVision(), Solid {
|
||||
public open class BasicSolid : VisionBase(), Solid {
|
||||
override val descriptor: NodeDescriptor get() = Solid.descriptor
|
||||
|
||||
@Serializable(Point3DSerializer::class)
|
||||
override var position: Point3D? = null
|
||||
|
||||
@ -19,16 +24,16 @@ public open class BasicSolid: AbstractVision(), Solid {
|
||||
@Serializable(Point3DSerializer::class)
|
||||
override var scale: Point3D? = null
|
||||
|
||||
override fun update(meta: Meta) {
|
||||
override fun update(change: Vision) {
|
||||
fun Meta.toVector(default: Float = 0f) = Point3D(
|
||||
this[Solid.X_KEY].float ?: default,
|
||||
this[Solid.Y_KEY].float ?: default,
|
||||
this[Solid.Z_KEY].float ?: default
|
||||
)
|
||||
|
||||
meta[Solid.POSITION_KEY].node?.toVector()?.let { position = it }
|
||||
meta[Solid.ROTATION].node?.toVector()?.let { rotation = it }
|
||||
meta[Solid.SCALE_KEY].node?.toVector(1f)?.let { scale = it }
|
||||
super.update(meta)
|
||||
change.properties[Solid.POSITION_KEY].node?.toVector()?.let { position = it }
|
||||
change.properties[Solid.ROTATION].node?.toVector()?.let { rotation = it }
|
||||
change.properties[Solid.SCALE_KEY].node?.toVector(1f)?.let { scale = it }
|
||||
super.update(change)
|
||||
}
|
||||
}
|
@ -7,6 +7,7 @@ import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.asName
|
||||
import hep.dataforge.names.plus
|
||||
import hep.dataforge.vision.VisionContainerBuilder
|
||||
import hep.dataforge.vision.properties
|
||||
import hep.dataforge.vision.set
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
@ -17,7 +18,7 @@ import kotlinx.serialization.UseSerializers
|
||||
public class PolyLine(public var points: List<Point3D>) : BasicSolid(), Solid {
|
||||
|
||||
//var lineType by string()
|
||||
public var thickness: Number by number(1.0, key = SolidMaterial.MATERIAL_KEY + THICKNESS_KEY)
|
||||
public var thickness: Number by properties().number(1.0, key = SolidMaterial.MATERIAL_KEY + THICKNESS_KEY)
|
||||
|
||||
public companion object {
|
||||
public val THICKNESS_KEY: Name = "thickness".asName()
|
||||
|
@ -10,14 +10,14 @@ import kotlinx.serialization.Transient
|
||||
import kotlin.collections.set
|
||||
|
||||
public abstract class AbstractProxy : BasicSolid(), VisionGroup {
|
||||
public abstract val prototype: Vision
|
||||
public abstract val prototype: Solid
|
||||
|
||||
override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? {
|
||||
return if (inherit) {
|
||||
sequence {
|
||||
yield(properties?.get(name))
|
||||
yieldAll(getStyleItems(name))
|
||||
yield(prototype.getItem(name))
|
||||
yield(prototype.getProperty(name))
|
||||
yield(parent?.getProperty(name, inherit))
|
||||
}.merge()
|
||||
} else {
|
||||
@ -42,7 +42,7 @@ public abstract class AbstractProxy : BasicSolid(), VisionGroup {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
override val descriptor: NodeDescriptor? get() = prototype.descriptor
|
||||
override val descriptor: NodeDescriptor get() = prototype.descriptor
|
||||
}
|
||||
|
||||
/**
|
||||
@ -82,13 +82,11 @@ public class Proxy private constructor(
|
||||
return NameToken(PROXY_CHILD_PROPERTY_PREFIX, childName.toString()) + propertyName
|
||||
}
|
||||
|
||||
private fun prototypeFor(name: Name): Vision {
|
||||
return (prototype as? VisionGroup)?.get(name)
|
||||
private fun prototypeFor(name: Name): Solid {
|
||||
return (prototype as? SolidGroup)?.get(name) as? Solid
|
||||
?: error("Prototype with name $name not found in $this")
|
||||
}
|
||||
|
||||
override val descriptor: NodeDescriptor? get() = prototype.descriptor
|
||||
|
||||
//override fun findAllStyles(): Laminate = Laminate((styles + prototype.styles).mapNotNull { findStyle(it) })
|
||||
|
||||
/**
|
||||
@ -97,7 +95,7 @@ public class Proxy private constructor(
|
||||
*/
|
||||
public inner class ProxyChild(public val name: Name) : AbstractProxy() {
|
||||
|
||||
override val prototype: Vision get() = prototypeFor(name)
|
||||
override val prototype: Solid get() = prototypeFor(name)
|
||||
|
||||
override val styleSheet: StyleSheet get() = this@Proxy.styleSheet
|
||||
|
||||
|
@ -14,7 +14,6 @@ import hep.dataforge.vision.enum
|
||||
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.LAYER_KEY
|
||||
import kotlinx.serialization.UseSerializers
|
||||
|
||||
/**
|
||||
* Interface for 3-dimensional [Vision]
|
||||
@ -24,7 +23,7 @@ public interface Solid : Vision {
|
||||
public var rotation: Point3D?
|
||||
public var scale: Point3D?
|
||||
|
||||
override val descriptor: NodeDescriptor? get() = Companion.descriptor
|
||||
override val descriptor: NodeDescriptor get() = Companion.descriptor
|
||||
|
||||
public companion object {
|
||||
// val SELECTED_KEY = "selected".asName()
|
||||
@ -123,7 +122,7 @@ public enum class RotationOrder {
|
||||
*/
|
||||
public var Solid.rotationOrder: RotationOrder
|
||||
get() = getItem(Solid.ROTATION_ORDER_KEY).enum<RotationOrder>() ?: RotationOrder.XYZ
|
||||
set(value) = setItem(Solid.ROTATION_ORDER_KEY, value.name.asValue())
|
||||
set(value) = setValue(Solid.ROTATION_ORDER_KEY, value.name.asValue())
|
||||
|
||||
|
||||
/**
|
||||
@ -131,7 +130,7 @@ public var Solid.rotationOrder: RotationOrder
|
||||
*/
|
||||
public var Solid.detail: Int?
|
||||
get() = getProperty(DETAIL_KEY, false).int
|
||||
set(value) = setItem(DETAIL_KEY, value?.asValue())
|
||||
set(value) = setValue(DETAIL_KEY, value?.asValue())
|
||||
|
||||
/**
|
||||
* If this property is true, the object will be ignored on render.
|
||||
@ -139,7 +138,7 @@ public var Solid.detail: Int?
|
||||
*/
|
||||
public var Vision.ignore: Boolean?
|
||||
get() = getProperty(IGNORE_KEY, false).boolean
|
||||
set(value) = setItem(IGNORE_KEY, value?.asValue())
|
||||
set(value) = setValue(IGNORE_KEY, value?.asValue())
|
||||
|
||||
//var VisualObject.selected: Boolean?
|
||||
// get() = getProperty(SELECTED_KEY).boolean
|
||||
|
@ -1,16 +1,12 @@
|
||||
package hep.dataforge.vision.solid
|
||||
|
||||
import hep.dataforge.meta.Config
|
||||
import hep.dataforge.meta.DFExperimental
|
||||
import hep.dataforge.meta.descriptors.NodeDescriptor
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.NameToken
|
||||
import hep.dataforge.names.asName
|
||||
import hep.dataforge.vision.*
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Serializer
|
||||
import kotlinx.serialization.UseSerializers
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlin.collections.set
|
||||
|
||||
public interface PrototypeHolder {
|
||||
@ -25,7 +21,7 @@ public interface PrototypeHolder {
|
||||
@SerialName("group.solid")
|
||||
public class SolidGroup : AbstractVisionGroup(), Solid, PrototypeHolder {
|
||||
|
||||
override var styleSheet: StyleSheet? = null
|
||||
override val descriptor: NodeDescriptor get() = Solid.descriptor
|
||||
|
||||
/**
|
||||
* A container for templates visible inside this group
|
||||
@ -44,9 +40,6 @@ public class SolidGroup : AbstractVisionGroup(), Solid, PrototypeHolder {
|
||||
}).run(builder)
|
||||
}
|
||||
|
||||
//FIXME to be lifted to AbstractVisualGroup after https://github.com/Kotlin/kotlinx.serialization/issues/378 is fixed
|
||||
override var properties: Config? = null
|
||||
|
||||
@Serializable(Point3DSerializer::class)
|
||||
override var position: Point3D? = null
|
||||
|
||||
@ -111,14 +104,12 @@ public fun MutableVisionGroup.group(name: String, action: SolidGroup.() -> Unit
|
||||
* A special class which works as a holder for prototypes
|
||||
*/
|
||||
internal class Prototypes(
|
||||
override var children: MutableMap<NameToken, Vision> = LinkedHashMap()
|
||||
override var children: MutableMap<NameToken, Vision> = LinkedHashMap(),
|
||||
) : AbstractVisionGroup(), MutableVisionGroup, PrototypeHolder {
|
||||
|
||||
override var styleSheet: StyleSheet?
|
||||
get() = null
|
||||
set(_) {
|
||||
error("Can't define stylesheet for prototypes block")
|
||||
}
|
||||
override fun styleSheet(block: StyleSheet.() -> Unit) {
|
||||
error("Can't define stylesheet for prototypes block")
|
||||
}
|
||||
|
||||
override fun removeChild(token: NameToken) {
|
||||
children.remove(token)
|
||||
|
@ -1,12 +1,8 @@
|
||||
package hep.dataforge.vision.solid
|
||||
|
||||
import hep.dataforge.meta.DFExperimental
|
||||
import hep.dataforge.meta.double
|
||||
import hep.dataforge.meta.transformations.MetaConverter.Companion.double
|
||||
import hep.dataforge.names.NameToken
|
||||
import hep.dataforge.vision.MutableVisionGroup
|
||||
import hep.dataforge.vision.Vision
|
||||
import hep.dataforge.vision.VisionGroup
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.builtins.MapSerializer
|
||||
import kotlinx.serialization.builtins.nullable
|
||||
@ -18,7 +14,6 @@ import kotlinx.serialization.encoding.*
|
||||
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
@Serializer(Point3D::class)
|
||||
public object Point3DSerializer : KSerializer<Point3D> {
|
||||
override val descriptor: SerialDescriptor = buildClassSerialDescriptor("hep.dataforge.vis.spatial.Point3D") {
|
||||
element<Double>("x")
|
||||
@ -54,7 +49,6 @@ public object Point3DSerializer : KSerializer<Point3D> {
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
@Serializer(Point2D::class)
|
||||
public object Point2DSerializer : KSerializer<Point2D> {
|
||||
override val descriptor: SerialDescriptor = buildClassSerialDescriptor("hep.dataforge.vis.spatial.Point2D") {
|
||||
element<Double>("x")
|
||||
@ -85,8 +79,6 @@ public object Point2DSerializer : KSerializer<Point2D> {
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
@Serializer(MutableVisionGroup::class)
|
||||
internal object PrototypesSerializer : KSerializer<MutableVisionGroup> {
|
||||
|
||||
private val mapSerializer: KSerializer<Map<NameToken, Vision>> =
|
||||
|
Loading…
Reference in New Issue
Block a user