Replace structure changes listener by callback. Fix review issues

This commit is contained in:
Alexander Nozik 2021-08-13 20:40:28 +03:00
parent c425f3b36f
commit c44162671f
7 changed files with 63 additions and 50 deletions

View File

@ -17,6 +17,7 @@ internal fun visionOfSatellite(
ySegmentSize: Number = xSegmentSize,
fiberDiameter: Number = 1.0,
): SolidGroup = SolidGroup {
color("darkgreen")
val transparent by style {
this[SolidMaterial.MATERIAL_OPACITY_KEY] = 0.3
}

View File

@ -23,7 +23,7 @@ internal data class MetaListener(
/**
* A full base implementation for a [Vision]
* @param properties Object own properties excluding styles and inheritance
* @param parent the parent object for this vision. Could ve set later. Not serialized.
*/
@Serializable
@SerialName("vision")

View File

@ -82,6 +82,7 @@ public inline fun VisionChange(manager: VisionManager, block: VisionChangeBuilde
VisionChangeBuilder().apply(block).isolate(manager)
@OptIn(DFExperimental::class)
private fun CoroutineScope.collectChange(
name: Name,
source: Vision,
@ -102,11 +103,13 @@ private fun CoroutineScope.collectChange(
//Subscribe for structure change
if (source is MutableVisionGroup) {
source.structureChanges.onEach { (token, _, after) ->
source.structureChanges.onEach { changedName ->
val after = source[changedName]
val fullName = name + changedName
if (after != null) {
collectChange(name + token, after, collector)
collectChange(fullName, after, collector)
}
collector()[name + token] = after
collector()[fullName] = after
}.launchIn(this)
}
}

View File

@ -1,6 +1,11 @@
package space.kscience.visionforge
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.launch
import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.names.*
import space.kscience.dataforge.provider.Provider
@ -69,15 +74,30 @@ public interface VisionContainerBuilder<in V : Vision> {
* Mutable version of [VisionGroup]
*/
public interface MutableVisionGroup : VisionGroup, VisionContainerBuilder<Vision> {
public fun onStructureChanged(owner: Any?, block: VisionGroup.(Name) -> Unit)
public data class StructureChange(val token: NameToken, val before: Vision?, val after: Vision?)
/**
* Flow structure changes of this group. Unconsumed changes are discarded
*/
public val structureChanges: Flow<StructureChange>
public fun removeStructureListener(owner: Any?)
}
/**
* Flow structure changes of this group. Unconsumed changes are discarded
*/
@OptIn(ExperimentalCoroutinesApi::class)
@DFExperimental
public val MutableVisionGroup.structureChanges: Flow<Name>
get() = callbackFlow {
meta.onChange(this) { name ->
launch {
send(name)
}
}
awaitClose {
removeStructureListener(this)
}
}
public operator fun <V : Vision> VisionContainer<V>.get(str: String): V? = get(Name.parse(str))
public operator fun <V : Vision> VisionContainerBuilder<V>.set(token: NameToken, child: V?): Unit =

View File

@ -1,13 +1,12 @@
package space.kscience.visionforge
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.launch
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import space.kscience.dataforge.names.*
import kotlin.jvm.Synchronized
private class StructureChangeListener(val owner: Any?, val callback: VisionGroup.(Name) -> Unit)
/**
* Abstract implementation of mutable group of [Vision]
@ -40,16 +39,24 @@ public open class VisionGroupBase(
}
@Transient
private val _structureChanges: MutableSharedFlow<MutableVisionGroup.StructureChange> = MutableSharedFlow()
private val structureListeners = HashSet<StructureChangeListener>()
override val structureChanges: SharedFlow<MutableVisionGroup.StructureChange> get() = _structureChanges
@Synchronized
override fun onStructureChanged(owner: Any?, block: VisionGroup.(Name) -> Unit) {
structureListeners.add(StructureChangeListener(owner, block))
}
@Synchronized
override fun removeStructureListener(owner: Any?) {
structureListeners.removeAll { it.owner == owner }
}
/**
* Propagate children change event upwards
*/
private fun childrenChanged(name: NameToken, before: Vision?, after: Vision?) {
(manager?.context?: GlobalScope).launch {
_structureChanges.emit(MutableVisionGroup.StructureChange(name, before, after))
protected fun childrenChanged(name: Name) {
structureListeners.forEach {
it.callback(this, name)
}
}
@ -83,7 +90,12 @@ public open class VisionGroupBase(
}
}
if (before != child) {
childrenChanged(token, before, child)
childrenChanged(token.asName())
if (child is MutableVisionGroup) {
child.onStructureChanged(this) { changedName ->
this@VisionGroupBase.childrenChanged(token + changedName)
}
}
}
}

View File

@ -49,28 +49,4 @@ class VisionUpdateTest {
val reconstructed = visionManager.jsonFormat.decodeFromString(VisionChange.serializer(), serialized)
assertEquals(change.properties,reconstructed.properties)
}
@Test
fun testDeserialization(){
val str = """
{
"propertyChange": {
"layer[4]": {
"material": {
"color": 123
}
},
"layer[2]": {
"material": {
}
}
},
"childrenChange": {
}
}
""".trimIndent()
val reconstructed = visionManager.jsonFormat.decodeFromString(VisionChange.serializer(), str)
}
}

View File

@ -2,8 +2,6 @@ package space.kscience.visionforge.solid.three
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.HTMLElement
import space.kscience.dataforge.context.*
@ -15,6 +13,7 @@ import space.kscience.visionforge.Vision
import space.kscience.visionforge.onPropertyChange
import space.kscience.visionforge.solid.*
import space.kscience.visionforge.solid.specifications.Canvas3DOptions
import space.kscience.visionforge.solid.three.set
import space.kscience.visionforge.visible
import kotlin.collections.set
import kotlin.reflect.KClass
@ -82,9 +81,11 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
}
}
obj.structureChanges.onEach { (nameToken, _, child) ->
obj.onStructureChanged(this){ childName ->
val child = get(childName)
//removing old object
findChild(nameToken.asName())?.let { oldChild ->
findChild(childName)?.let { oldChild ->
oldChild.parent?.remove(oldChild)
}
@ -92,12 +93,12 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
if (child != null && child is Solid) {
try {
val object3D = buildObject3D(child)
set(nameToken, object3D)
set(childName, object3D)
} catch (ex: Throwable) {
logger.error(ex) { "Failed to render $child" }
}
}
}.launchIn(updateScope)
}
}
}
is Composite -> compositeFactory(this, obj)