0.2.0 #71
@ -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
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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 =
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user