From dfac01d3524f4cddf1b2eee46ea46a81a34237fa Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Thu, 12 Nov 2020 12:05:43 +0300 Subject: [PATCH] Change collector update --- CHANGELOG.md | 2 + build.gradle.kts | 4 +- .../dataforge/vision/AbstractVisionGroup.kt | 47 ++++---- .../hep/dataforge/vision/SimpleVisionGroup.kt | 6 +- .../kotlin/hep/dataforge/vision/StyleSheet.kt | 30 +++-- .../kotlin/hep/dataforge/vision/Vision.kt | 68 ++++++++--- .../{AbstractVision.kt => VisionBase.kt} | 18 ++- .../hep/dataforge/vision/VisionChange.kt | 50 --------- .../hep/dataforge/vision/VisionManager.kt | 52 ++++----- .../hep/dataforge/vision/collectChange.kt | 106 ++++++++++++++++++ visionforge-server/build.gradle.kts | 57 +--------- .../hep/dataforge/vision/solid/BasicSolid.kt | 23 ++-- .../hep/dataforge/vision/solid/PolyLine.kt | 3 +- .../hep/dataforge/vision/solid/Proxy.kt | 14 +-- .../hep/dataforge/vision/solid/Solid.kt | 9 +- .../hep/dataforge/vision/solid/SolidGroup.kt | 21 +--- .../dataforge/vision/solid/serialization.kt | 8 -- 17 files changed, 283 insertions(+), 235 deletions(-) rename visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/{AbstractVision.kt => VisionBase.kt} (85%) delete mode 100644 visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionChange.kt create mode 100644 visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/collectChange.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index c3200775..a158a449 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/build.gradle.kts b/build.gradle.kts index 25d9f4b9..b793c216 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -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 { diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/AbstractVisionGroup.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/AbstractVisionGroup.kt index c6d05e8c..aa43a14e 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/AbstractVisionGroup.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/AbstractVisionGroup.kt @@ -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 @@ -19,13 +16,13 @@ public abstract class AbstractVisionGroup : AbstractVision(), MutableVisionGroup */ abstract override val children: Map - 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) } } \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/SimpleVisionGroup.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/SimpleVisionGroup.kt index 69a28f07..96c2325c 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/SimpleVisionGroup.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/SimpleVisionGroup.kt @@ -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() override val children: Map get() = _children diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/StyleSheet.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/StyleSheet.kt index fa5ef9e9..d154efbd 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/StyleSheet.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/StyleSheet.kt @@ -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) { @Transient internal var owner: Vision? = null @@ -30,8 +33,9 @@ public class StyleSheet private constructor(private val styleMap: MutableMap = ((oldStyle?.items?.keys ?: emptySet()) + (newStyle?.items?.keys ?: emptySet())) - .map { it.asName() } + val tokens: Collection = + ((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 + update(key, value) + } + } + public companion object : KSerializer { 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? - - /** - * 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 - 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?> { + 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) + } + +} \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/AbstractVision.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt similarity index 85% rename from visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/AbstractVision.kt rename to visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt index 50bcc9b7..d88aaffc 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/AbstractVision.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt @@ -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) { 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 { diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionChange.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionChange.kt deleted file mode 100644 index 20d28123..00000000 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionChange.kt +++ /dev/null @@ -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) - -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() - - 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 { -// -// -// vision. -//} diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionManager.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionManager.kt index 3e417935..1078abd6 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionManager.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionManager.kt @@ -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 { override val tag: PluginTag = PluginTag(name = "vision", group = PluginTag.DATAFORGE_GROUP) diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/collectChange.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/collectChange.kt new file mode 100644 index 00000000..e2a128bc --- /dev/null +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/collectChange.kt @@ -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() + + 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 = 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() + } +} \ No newline at end of file diff --git a/visionforge-server/build.gradle.kts b/visionforge-server/build.gradle.kts index 60d5624b..17696344 100644 --- a/visionforge-server/build.gradle.kts +++ b/visionforge-server/build.gradle.kts @@ -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(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") -// } -// } -// } -//} \ No newline at end of file diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/BasicSolid.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/BasicSolid.kt index 7cd198bb..40f2543e 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/BasicSolid.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/BasicSolid.kt @@ -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) } } \ No newline at end of file diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/PolyLine.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/PolyLine.kt index 1a6769b8..957e4501 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/PolyLine.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/PolyLine.kt @@ -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) : 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() diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Proxy.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Proxy.kt index 9f58912b..6bc250d8 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Proxy.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Proxy.kt @@ -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 diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Solid.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Solid.kt index 7e9c044b..e7d3587f 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Solid.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Solid.kt @@ -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.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 diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt index f27ea3f3..381a2f5c 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt @@ -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 = LinkedHashMap() + override var children: MutableMap = 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) diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/serialization.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/serialization.kt index e24ccc30..c5b0a435 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/serialization.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/serialization.kt @@ -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 { override val descriptor: SerialDescriptor = buildClassSerialDescriptor("hep.dataforge.vis.spatial.Point3D") { element("x") @@ -54,7 +49,6 @@ public object Point3DSerializer : KSerializer { } @OptIn(ExperimentalSerializationApi::class) -@Serializer(Point2D::class) public object Point2DSerializer : KSerializer { override val descriptor: SerialDescriptor = buildClassSerialDescriptor("hep.dataforge.vis.spatial.Point2D") { element("x") @@ -85,8 +79,6 @@ public object Point2DSerializer : KSerializer { } } -@OptIn(ExperimentalSerializationApi::class) -@Serializer(MutableVisionGroup::class) internal object PrototypesSerializer : KSerializer { private val mapSerializer: KSerializer> =