diff --git a/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/satServer.kt b/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/satServer.kt index 761cd982..a605e717 100644 --- a/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/satServer.kt +++ b/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/satServer.kt @@ -3,6 +3,7 @@ package ru.mipt.npm.sat import hep.dataforge.names.toName import hep.dataforge.vision.solid.Solid +import hep.dataforge.vision.solid.clear import hep.dataforge.vision.solid.color import hep.dataforge.vision.solid.invoke import hep.dataforge.vision.three.server.* @@ -42,7 +43,7 @@ fun main() { val targetVision = sat[target] as Solid targetVision.color("red") delay(300) - targetVision.color("darkgreen") + targetVision.color.clear() delay(10) } } diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt index 28f6b988..39d5ae5a 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt @@ -57,6 +57,9 @@ public interface Vision : Described { */ public fun setProperty(name: Name, item: MetaItem<*>?, notify: Boolean = true) + /** + * Subscribe on property updates. The subscription is bound to the given [scope] and canceled when the scope is canceled + */ public fun onPropertyChange(scope: CoroutineScope, callback: suspend (Name) -> Unit) /** @@ -81,7 +84,7 @@ public interface Vision : Described { public suspend fun notifyPropertyChanged(propertyName: Name): Unit /** - * Update this vision using external meta. Children are not updated. + * Update this vision using a dif represented by [VisionChange]. */ public fun update(change: VisionChange) diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt index fdd7feed..a70cdf21 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt @@ -3,12 +3,14 @@ package hep.dataforge.vision import hep.dataforge.meta.Config import hep.dataforge.meta.MetaItem import hep.dataforge.meta.MutableMeta +import hep.dataforge.meta.asMetaItem import hep.dataforge.meta.descriptors.NodeDescriptor import hep.dataforge.meta.descriptors.defaultItem import hep.dataforge.meta.descriptors.get -import hep.dataforge.meta.update import hep.dataforge.names.Name import hep.dataforge.names.asName +import hep.dataforge.names.plus +import hep.dataforge.values.Null import hep.dataforge.values.ValueType import hep.dataforge.vision.Vision.Companion.STYLE_KEY import kotlinx.coroutines.CoroutineScope @@ -27,6 +29,7 @@ internal data class PropertyListener( val action: (name: Name) -> Unit, ) + @Serializable @SerialName("vision") public open class VisionBase : Vision { @@ -100,6 +103,8 @@ public open class VisionBase : Vision { } } + + //TODO check memory consumption for the flow @Transient private val propertyInvalidationFlow: MutableSharedFlow = MutableSharedFlow() @@ -116,13 +121,30 @@ public open class VisionBase : Vision { propertyInvalidationFlow.emit(propertyName) } - public fun configure(block: MutableMeta<*>.() -> Unit) { - getOrCreateConfig().block() + public fun configure(block: suspend MutableMeta<*>.() -> Unit) { + scope.launch { + getOrCreateConfig().block() + } } override fun update(change: VisionChange) { + + fun updateProperties(at: Name, item: MetaItem<*>) { + when (item) { + is MetaItem.ValueItem -> { + if (item.value == Null) { + setProperty(at, null) + } else + setProperty(at, item) + } + is MetaItem.NodeItem -> item.node.items.forEach { (token, childItem) -> + updateProperties(at + token, childItem) + } + } + } + change.properties?.let { - getOrCreateConfig().update(it) + updateProperties(Name.EMPTY, it.asMetaItem()) } } diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionChange.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionChange.kt index cbff844c..c0cc594a 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionChange.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionChange.kt @@ -3,6 +3,7 @@ package hep.dataforge.vision import hep.dataforge.meta.* import hep.dataforge.names.Name import hep.dataforge.names.plus +import hep.dataforge.values.Null import kotlinx.coroutines.* import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow @@ -30,7 +31,8 @@ public class VisionChangeBuilder : VisionContainerBuilder { public fun propertyChanged(visionName: Name, propertyName: Name, item: MetaItem<*>?) { if (visionName == Name.EMPTY) { - propertyChange[propertyName] = item + //Write property removal as [Null] + propertyChange[propertyName] = (item ?: Null.asMetaItem()) } else { getOrPutChild(visionName).propertyChanged(Name.EMPTY, propertyName, item) } @@ -112,6 +114,10 @@ public fun Vision.flowChanges( coroutineScope { collectChange(Name.EMPTY, this@flowChanges) { collector } + //Send initial vision state + val initialChange = VisionChange(vision = isolate(manager)) + emit(initialChange) + while (currentCoroutineContext().isActive) { //Wait for changes to accumulate delay(collectionDuration) diff --git a/visionforge-server/src/main/kotlin/hep/dataforge/vision/three/server/VisionServer.kt b/visionforge-server/src/main/kotlin/hep/dataforge/vision/three/server/VisionServer.kt index 13528ff7..5baf2643 100644 --- a/visionforge-server/src/main/kotlin/hep/dataforge/vision/three/server/VisionServer.kt +++ b/visionforge-server/src/main/kotlin/hep/dataforge/vision/three/server/VisionServer.kt @@ -134,14 +134,6 @@ public class VisionServer internal constructor( try { withContext(visionManager.context.coroutineContext) { - - val initialVision = VisionChange(vision = vision) - val initialJson = visionManager.jsonFormat.encodeToString( - VisionChange.serializer(), - initialVision - ) - outgoing.send(Frame.Text(initialJson)) - vision.flowChanges(visionManager, updateInterval.milliseconds).collect { update -> val json = visionManager.jsonFormat.encodeToString( VisionChange.serializer(), diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/ColorAccessor.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/ColorAccessor.kt new file mode 100644 index 00000000..3b78d7b5 --- /dev/null +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/ColorAccessor.kt @@ -0,0 +1,51 @@ +package hep.dataforge.vision.solid + +import hep.dataforge.meta.MutableItemProvider +import hep.dataforge.meta.set +import hep.dataforge.meta.value +import hep.dataforge.names.Name +import hep.dataforge.values.Value +import hep.dataforge.values.asValue +import hep.dataforge.values.string +import hep.dataforge.vision.Colors +import hep.dataforge.vision.VisionBuilder + +@VisionBuilder +public class ColorAccessor(private val parent: MutableItemProvider, private val colorKey: Name) { + public var value: Value? + get() = parent.getItem(colorKey).value + set(value) { + parent[colorKey] = value + } +} + +public var ColorAccessor?.string: String? + get() = this?.value?.string + set(value) { + this?.value = value?.asValue() + } + +/** + * Set [webcolor](https://en.wikipedia.org/wiki/Web_colors) as string + */ +public operator fun ColorAccessor?.invoke(webColor: String) { + this?.value = webColor.asValue() +} + +/** + * Set color as RGB integer + */ +public operator fun ColorAccessor?.invoke(rgb: Int) { + this?.value = Colors.rgbToString(rgb).asValue() +} + +/** + * Set color as RGB + */ +public operator fun ColorAccessor?.invoke(r: UByte, g: UByte, b: UByte) { + this?.value = Colors.rgbToString(r, g, b).asValue() +} + +public fun ColorAccessor?.clear(){ + this?.value = null +} \ No newline at end of file diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidMaterial.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidMaterial.kt index d845df8b..2394030b 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidMaterial.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidMaterial.kt @@ -6,51 +6,13 @@ import hep.dataforge.meta.descriptors.attributes import hep.dataforge.names.Name import hep.dataforge.names.asName import hep.dataforge.names.plus -import hep.dataforge.values.Value import hep.dataforge.values.ValueType import hep.dataforge.values.asValue -import hep.dataforge.values.string import hep.dataforge.vision.* 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_OPACITY_KEY -@VisionBuilder -public class ColorAccessor(private val parent: MutableItemProvider, private val colorKey: Name) { - public var value: Value? - get() = parent.getItem(colorKey).value - set(value) { - parent[colorKey] = value - } -} - -public var ColorAccessor?.string: String? - get() = this?.value?.string - set(value) { - this?.value = value?.asValue() - } - -/** - * Set [webcolor](https://en.wikipedia.org/wiki/Web_colors) as string - */ -public operator fun ColorAccessor?.invoke(webColor: String) { - this?.value = webColor.asValue() -} - -/** - * Set color as RGB integer - */ -public operator fun ColorAccessor?.invoke(rgb: Int) { - this?.value = Colors.rgbToString(rgb).asValue() -} - -/** - * Set color as RGB - */ -public operator fun ColorAccessor?.invoke(r: UByte, g: UByte, b: UByte) { - this?.value = Colors.rgbToString(r, g, b).asValue() -} - @VisionBuilder public class SolidMaterial : Scheme() {