Fix plotly update problem

This commit is contained in:
Alexander Nozik 2024-03-14 14:54:16 +03:00
parent 1145483a90
commit 6dc1b9f349
9 changed files with 63 additions and 49 deletions

View File

@ -1,7 +1,6 @@
kotlin.code.style=official kotlin.code.style=official
kotlin.mpp.stability.nowarn=true kotlin.mpp.stability.nowarn=true
kotlin.js.compiler=ir kotlin.js.compiler=ir
#kotlin.incremental.js.ir=true
org.gradle.parallel=true org.gradle.parallel=true
org.gradle.jvmargs=-Xmx4G org.gradle.jvmargs=-Xmx4G

View File

@ -147,7 +147,7 @@ private fun CoroutineScope.collectChange(
) { ) {
//Collect properties change //Collect properties change
source.properties.flowChanges().onEach { propertyName -> source.properties.changes.onEach { propertyName ->
val newItem = source.properties.own[propertyName] val newItem = source.properties.own[propertyName]
collector.propertyChanged(name, propertyName, newItem) collector.propertyChanged(name, propertyName, newItem)
}.launchIn(this) }.launchIn(this)

View File

@ -40,7 +40,11 @@ public interface VisionProperties : MetaProvider {
override fun get(name: Name): Meta? = get(name, null, null) override fun get(name: Name): Meta? = get(name, null, null)
public fun flowChanges(): Flow<Name> public val changes: Flow<Name>
@Deprecated("Replace with property", ReplaceWith("changes"))
public fun flowChanges(): Flow<Name> = changes
/** /**
* Notify all listeners that a property has been changed and should be invalidated. * Notify all listeners that a property has been changed and should be invalidated.
@ -64,7 +68,7 @@ public interface MutableVisionProperties : VisionProperties, MutableMetaProvider
public fun set( public fun set(
name: Name, name: Name,
node: Meta?, item: Meta?,
notify: Boolean, notify: Boolean,
) )
@ -186,28 +190,28 @@ public open class AbstractVisionProperties(
return descriptor?.defaultValue return descriptor?.defaultValue
} }
override fun set(name: Name, node: Meta?, notify: Boolean) { override fun set(name: Name, item: Meta?, notify: Boolean) {
//ignore if the value is the same as existing //ignore if the value is the same as existing
if (own[name] == node) return if (own[name] == item) return
if (name.isEmpty()) { if (name.isEmpty()) {
if (node == null) { if (item == null) {
own.items.keys.forEach { own.items.keys.forEach {
remove(it.asName()) remove(it.asName())
} }
} else { } else {
(own.items.keys - node.items.keys).forEach { (own.items.keys - item.items.keys).forEach {
remove(it.asName()) remove(it.asName())
} }
node.items.forEach { (token, item) -> item.items.forEach { (token, item) ->
set(token, item) set(token, item)
} }
} }
} else if (node == null) { } else if (item == null) {
own[name] = node own[name] = item
} else { } else {
own[name] = node own[name] = item
} }
if (notify) { if (notify) {
invalidate(name) invalidate(name)
@ -231,7 +235,8 @@ public open class AbstractVisionProperties(
@Transient @Transient
protected val changesInternal: MutableSharedFlow<Name> = MutableSharedFlow() protected val changesInternal: MutableSharedFlow<Name> = MutableSharedFlow()
override fun flowChanges(): Flow<Name> = changesInternal override val changes: Flow<Name>
get() = changesInternal
override fun invalidate(propertyName: Name) { override fun invalidate(propertyName: Name) {
//send update signal //send update signal

View File

@ -18,7 +18,7 @@ public fun Vision.flowProperty(
): Flow<Meta> = flow { ): Flow<Meta> = flow {
//Pass initial value. //Pass initial value.
emit(properties.get(propertyName, inherit, includeStyles)) emit(properties.get(propertyName, inherit, includeStyles))
properties.flowChanges().collect { name -> properties.changes.collect { name ->
if (name.startsWith(propertyName)) { if (name.startsWith(propertyName)) {
emit(properties.get(propertyName, inherit, includeStyles)) emit(properties.get(propertyName, inherit, includeStyles))
} }
@ -41,7 +41,7 @@ public fun Vision.flowPropertyValue(
): Flow<Value?> = flow { ): Flow<Value?> = flow {
//Pass initial value. //Pass initial value.
emit(properties.getValue(propertyName, inherit, includeStyles)) emit(properties.getValue(propertyName, inherit, includeStyles))
properties.flowChanges().collect { name -> properties.changes.collect { name ->
if (name.startsWith(propertyName)) { if (name.startsWith(propertyName)) {
emit(properties.getValue(propertyName, inherit, includeStyles)) emit(properties.getValue(propertyName, inherit, includeStyles))
} }

View File

@ -25,7 +25,7 @@ public fun Vision.useProperty(
): Job { ): Job {
//Pass initial value. //Pass initial value.
callback(properties.get(propertyName, inherit, includeStyles)) callback(properties.get(propertyName, inherit, includeStyles))
return properties.flowChanges().onEach { name -> return properties.changes.onEach { name ->
if (name.startsWith(propertyName)) { if (name.startsWith(propertyName)) {
callback(properties.get(propertyName, inherit, includeStyles)) callback(properties.get(propertyName, inherit, includeStyles))
} }
@ -47,7 +47,7 @@ public fun <V : Vision, T> V.useProperty(
): Job { ): Job {
//Pass initial value. //Pass initial value.
callback(property.get(this)) callback(property.get(this))
return properties.flowChanges().onEach { name -> return properties.changes.onEach { name ->
if (name.startsWith(property.name.asName())) { if (name.startsWith(property.name.asName())) {
callback(property.get(this@useProperty)) callback(property.get(this@useProperty))
} }
@ -60,7 +60,7 @@ public fun <V : Vision, T> V.useProperty(
public fun Vision.onPropertyChange( public fun Vision.onPropertyChange(
scope: CoroutineScope = manager?.context ?: error("Orphan Vision can't observe properties. Use explicit scope."), scope: CoroutineScope = manager?.context ?: error("Orphan Vision can't observe properties. Use explicit scope."),
callback: suspend (Name) -> Unit, callback: suspend (Name) -> Unit,
): Job = properties.flowChanges().onEach { ): Job = properties.changes.onEach {
callback(it) callback(it)
}.launchIn(scope) }.launchIn(scope)
@ -71,6 +71,6 @@ public fun <V : Vision, T> V.onPropertyChange(
property: KProperty1<V, T>, property: KProperty1<V, T>,
scope: CoroutineScope = manager?.context ?: error("Orphan Vision can't observe properties. Use explicit scope."), scope: CoroutineScope = manager?.context ?: error("Orphan Vision can't observe properties. Use explicit scope."),
callback: suspend V.(T) -> Unit, callback: suspend V.(T) -> Unit,
): Job = properties.flowChanges().filter { it.startsWith(property.name.asName()) }.onEach { ): Job = properties.changes.filter { it.startsWith(property.name.asName()) }.onEach {
callback(property.get(this)) callback(property.get(this))
}.launchIn(scope) }.launchIn(scope)

View File

@ -1,23 +1,17 @@
package space.kscience.visionforge.plotly package space.kscience.visionforge.plotly
import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
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 space.kscience.dataforge.meta.MutableMeta import space.kscience.dataforge.meta.*
import space.kscience.dataforge.meta.MutableMetaSerializer
import space.kscience.dataforge.meta.ObservableMeta
import space.kscience.dataforge.meta.asObservable
import space.kscience.dataforge.meta.descriptors.MetaDescriptor import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.plotly.Plot import space.kscience.plotly.Plot
import space.kscience.plotly.Plotly import space.kscience.plotly.Plotly
import space.kscience.plotly.PlotlyConfig import space.kscience.plotly.PlotlyConfig
import space.kscience.visionforge.AbstractVisionProperties
import space.kscience.visionforge.MutableVisionProperties import space.kscience.visionforge.MutableVisionProperties
import space.kscience.visionforge.Vision import space.kscience.visionforge.Vision
import space.kscience.visionforge.VisionBuilder import space.kscience.visionforge.VisionBuilder
@ -26,36 +20,52 @@ import space.kscience.visionforge.html.VisionOutput
@Serializable @Serializable
@SerialName("vision.plotly") @SerialName("vision.plotly")
public class VisionOfPlotly private constructor( public class VisionOfPlotly private constructor(
@Serializable(MutableMetaSerializer::class) public val meta: MutableMeta, @Serializable(MutableMetaSerializer::class) private val meta: MutableMeta,
) : Vision { ) : Vision {
public constructor(plot: Plot) : this(plot.meta) public constructor(plot: Plot) : this(plot.meta)
public val plot: Plot get() = Plot(meta.asObservable()) @Transient
public val plot: Plot = Plot(meta.asObservable())
@Transient @Transient
override var parent: Vision? = null override var parent: Vision? = null
@Transient @Transient
override val properties: MutableVisionProperties = object : AbstractVisionProperties(this, meta) { override val properties: MutableVisionProperties = object : MutableVisionProperties {
override val own: Meta get() = plot.meta
override fun flowChanges(): Flow<Name> = if (meta is ObservableMeta) { override val changes = callbackFlow {
callbackFlow { plot.meta.onChange(this) {
meta.onChange(this) { println(it)
launch { launch {
send(it) send(it)
} }
} }
awaitClose { awaitClose {
meta.removeListener(this) plot.meta.removeListener(this)
} }
} }
} else emptyFlow()
override fun invalidate(propertyName: Name) { override fun invalidate(propertyName: Name) {
// Do nothing //do nothing, updates to source already counted
// manager?.context?.launch {
// changes.emit(propertyName)
// }
} }
override fun getValue(name: Name, inherit: Boolean?, includeStyles: Boolean?): Value? = plot.meta[name]?.value
override fun set(name: Name, item: Meta?, notify: Boolean) {
plot.meta[name] = item
if (notify) invalidate(name)
}
override fun setValue(name: Name, value: Value?, notify: Boolean) {
plot.meta[name] = value
if (notify) invalidate(name)
}
override val descriptor: MetaDescriptor get() = plot.descriptor
} }

View File

@ -166,16 +166,16 @@ internal class SolidReferenceChild(
includeStyles: Boolean?, includeStyles: Boolean?,
): Value? = own.getValue(name) ?: prototype.properties.getValue(name, inherit, includeStyles) ): Value? = own.getValue(name) ?: prototype.properties.getValue(name, inherit, includeStyles)
override fun set(name: Name, node: Meta?, notify: Boolean) { override fun set(name: Name, item: Meta?, notify: Boolean) {
own[name] = node own[name] = item
} }
override fun setValue(name: Name, value: Value?, notify: Boolean) { override fun setValue(name: Name, value: Value?, notify: Boolean) {
own.setValue(name, value) own.setValue(name, value)
} }
override fun flowChanges(): Flow<Name> = override val changes: Flow<Name>
owner.properties.flowChanges().filter { it.startsWith(childToken(childName)) } get() = owner.properties.changes.filter { it.startsWith(childToken(childName)) }
override fun invalidate(propertyName: Name) { override fun invalidate(propertyName: Name) {
owner.properties.invalidate(childPropertyName(childName, propertyName)) owner.properties.invalidate(childPropertyName(childName, propertyName))

View File

@ -89,7 +89,7 @@ public class ThreePlugin : AbstractPlugin(), ComposeHtmlVisionRenderer {
updatePosition(vision) updatePosition(vision)
//obj.onChildrenChange() //obj.onChildrenChange()
if (observe) { if (observe) {
vision.properties.flowChanges().onEach { name -> vision.properties.changes.onEach { name ->
if ( if (
name.startsWith(Solid.POSITION_KEY) || name.startsWith(Solid.POSITION_KEY) ||
name.startsWith(Solid.ROTATION_KEY) || name.startsWith(Solid.ROTATION_KEY) ||

View File

@ -147,7 +147,7 @@ public fun ThreeView(
} }
}, },
name = Name.EMPTY, name = Name.EMPTY,
updates = vision.properties.flowChanges(), updates = vision.properties.changes,
rootDescriptor = vision.descriptor rootDescriptor = vision.descriptor
) )
vision.styles.takeIf { it.isNotEmpty() }?.let { styles -> vision.styles.takeIf { it.isNotEmpty() }?.let { styles ->