diff --git a/demo/playground/src/jvmMain/kotlin/formServer.kt b/demo/playground/src/jvmMain/kotlin/formServer.kt index 7397b6b6..accd1a16 100644 --- a/demo/playground/src/jvmMain/kotlin/formServer.kt +++ b/demo/playground/src/jvmMain/kotlin/formServer.kt @@ -49,7 +49,7 @@ fun main() { } vision("form") { form } - form.onPropertyChange { + form.onPropertyChange { _, _ -> println(this) } } diff --git a/demo/solid-showcase/src/jsMain/kotlin/space/kscience/visionforge/solid/demo/VariableBox.kt b/demo/solid-showcase/src/jsMain/kotlin/space/kscience/visionforge/solid/demo/VariableBox.kt index e55b1c8d..28b0c968 100644 --- a/demo/solid-showcase/src/jsMain/kotlin/space/kscience/visionforge/solid/demo/VariableBox.kt +++ b/demo/solid-showcase/src/jsMain/kotlin/space/kscience/visionforge/solid/demo/VariableBox.kt @@ -59,6 +59,7 @@ internal class VariableBox(val xSize: Number, val ySize: Number) : ThreeJsVision material.color.setRGB(r.toFloat() / 256, g.toFloat() / 256, b.toFloat() / 256) mesh.updateMatrix() } + name.startsWith(ThreeMeshFactory.EDGES_KEY) -> mesh.applyEdges(this@VariableBox) else -> mesh.updateProperty(this@VariableBox, name) } diff --git a/ui/react/src/main/kotlin/space/kscience/visionforge/react/PropertyEditor.kt b/ui/react/src/main/kotlin/space/kscience/visionforge/react/PropertyEditor.kt index 4e94ef06..ca64fd07 100644 --- a/ui/react/src/main/kotlin/space/kscience/visionforge/react/PropertyEditor.kt +++ b/ui/react/src/main/kotlin/space/kscience/visionforge/react/PropertyEditor.kt @@ -74,6 +74,8 @@ private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) { val descriptor: MetaDescriptor? = useMemo(props.descriptor, props.name) { props.descriptor?.get(props.name) } var property: MutableMeta by useState { props.meta.getOrCreate(props.name) } + val defined = props.getPropertyState(props.name) == EditorPropertyState.Defined + val keys = useMemo(descriptor) { buildSet { descriptor?.children?.filterNot { @@ -134,7 +136,7 @@ private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) { styledSpan { css { +TreeStyles.treeLabel - if (property.isEmpty()) { + if (!defined) { +TreeStyles.treeLabelInactive } } @@ -175,7 +177,7 @@ private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) { } +"\u00D7" attrs { - if (property.isEmpty()) { + if (!defined) { disabled = true } else { onClickFunction = removeClick diff --git a/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ringThreeControls.kt b/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ringThreeControls.kt index 1b62bd1f..6a440e71 100644 --- a/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ringThreeControls.kt +++ b/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ringThreeControls.kt @@ -1,5 +1,6 @@ package space.kscience.visionforge.ring +import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.GlobalScope import kotlinx.css.BorderStyle import kotlinx.css.Color @@ -52,6 +53,7 @@ internal external interface CanvasControlsProps : Props { public var vision: Vision? } +@OptIn(DelicateCoroutinesApi::class) internal val CanvasControls: FC = fc("CanvasControls") { props -> flexColumn { flexRow { diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/StyleReference.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/StyleReference.kt index 0e2b6132..62d898b8 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/StyleReference.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/StyleReference.kt @@ -18,10 +18,10 @@ private tailrec fun styleIsDefined(vision: Vision, reference: StyleReference): B } @VisionBuilder -public fun Vision.useStyle(reference: StyleReference) { +public fun Vision.useStyle(reference: StyleReference, notify: Boolean = true) { //check that style is defined in a parent //check(styleIsDefined(this, reference)) { "Style reference does not belong to a Vision parent" } - useStyle(reference.name) + useStyle(reference.name, notify) } @VisionBuilder diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/StyleSheet.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/StyleSheet.kt index 1c05d6d6..11e76d0c 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/StyleSheet.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/StyleSheet.kt @@ -83,10 +83,12 @@ public var Vision.styles: List public val Vision.styleSheet: StyleSheet get() = StyleSheet(this) /** - * Add style name to the list of styles to be resolved later. The style with given name does not necessary exist at the moment. + * Add style name to the list of styles to be resolved later. + * The style with given name does not necessary exist at the moment. */ -public fun Vision.useStyle(name: String) { - styles = (properties.own?.get(Vision.STYLE_KEY)?.stringList ?: emptyList()) + name +public fun Vision.useStyle(name: String, notify: Boolean = true) { + val newStyle = properties.own?.get(Vision.STYLE_KEY)?.value?.list?.plus(name.asValue()) ?: listOf(name.asValue()) + properties.setValue(Vision.STYLE_KEY, newStyle.asValue(), notify) } diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionChange.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionChange.kt index 29c38ebd..cfd522b3 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionChange.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionChange.kt @@ -119,7 +119,7 @@ private fun CoroutineScope.collectChange( public fun Vision.flowChanges( collectionDuration: Duration, ): Flow = flow { - val manager = manager?: error("Orphan vision could not collect changes") + val manager = manager ?: error("Orphan vision could not collect changes") var collector = VisionChangeBuilder(manager) coroutineScope { diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionProperties.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionProperties.kt index 1f751d55..c15c6c55 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionProperties.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionProperties.kt @@ -61,23 +61,24 @@ public interface MutableVisionProperties : VisionProperties { includeStyles, ) - public fun setProperty( name: Name, node: Meta?, + notify: Boolean = true, ) public fun setValue( name: Name, value: Value?, + notify: Boolean = true, ) } -public fun MutableVisionProperties.remove(name: Name){ +public fun MutableVisionProperties.remove(name: Name) { setProperty(name, null) } -public fun MutableVisionProperties.remove(name: String){ +public fun MutableVisionProperties.remove(name: String) { remove(name.parseAsName()) } @@ -180,7 +181,7 @@ public abstract class AbstractVisionProperties( return descriptor?.defaultValue } - override fun setProperty(name: Name, node: Meta?) { + override fun setProperty(name: Name, node: Meta?, notify: Boolean) { //TODO check old value? if (name.isEmpty()) { properties = node?.asMutableMeta() @@ -189,25 +190,42 @@ public abstract class AbstractVisionProperties( } else { getOrCreateProperties().setMeta(name, node) } - invalidate(name) + if (notify) { + invalidate(name) + } } - override fun setValue(name: Name, value: Value?) { + override fun setValue(name: Name, value: Value?, notify: Boolean) { //TODO check old value? if (value == null) { properties?.getMeta(name)?.value = null } else { getOrCreateProperties().setValue(name, value) } - invalidate(name) + if (notify) { + invalidate(name) + } } @Transient - private val _changes = MutableSharedFlow() - override val changes: SharedFlow get() = _changes + protected val changesInternal = MutableSharedFlow() + override val changes: SharedFlow get() = changesInternal - @OptIn(DelicateCoroutinesApi::class) override fun invalidate(propertyName: Name) { + //send update signal + @OptIn(DelicateCoroutinesApi::class) + (vision.manager?.context ?: GlobalScope).launch { + changesInternal.emit(propertyName) + } + + //notify children if there are any + if (vision is VisionGroup) { + vision.children.values.forEach { + it.properties.invalidate(propertyName) + } + } + + // update styles if (propertyName == Vision.STYLE_KEY) { vision.styles.asSequence() .mapNotNull { vision.getStyle(it) } @@ -217,9 +235,6 @@ public abstract class AbstractVisionProperties( invalidate(it.key.asName()) } } - (vision.manager?.context ?: GlobalScope).launch { - _changes.emit(propertyName) - } } } diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/VisionTagConsumer.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/VisionTagConsumer.kt index afdd686f..df0b9391 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/VisionTagConsumer.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/VisionTagConsumer.kt @@ -86,7 +86,9 @@ public abstract class VisionTagConsumer( ): T = div { id = resolveId(name) classes = setOf(OUTPUT_CLASS) - vision.setAsRoot(manager) + if (vision.parent == null) { + vision.setAsRoot(manager) + } attributes[OUTPUT_NAME_ATTRIBUTE] = name.toString() if (!outputMeta.isEmpty()) { //Hard-code output configuration diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/useProperty.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/useProperty.kt index b7cec960..4d00f15b 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/useProperty.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/useProperty.kt @@ -37,7 +37,7 @@ public fun Vision.useProperty( includeStyles: Boolean? = null, scope: CoroutineScope? = manager?.context, callBack: (Meta) -> Unit, -): Job = useProperty(propertyName.parseAsName(),inherit, includeStyles, scope, callBack) +): Job = useProperty(propertyName.parseAsName(), inherit, includeStyles, scope, callBack) public fun V.useProperty( property: KProperty1, diff --git a/visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/meta/VisionPropertyTest.kt b/visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/meta/VisionPropertyTest.kt index 64f0d0b2..90c72b9b 100644 --- a/visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/meta/VisionPropertyTest.kt +++ b/visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/meta/VisionPropertyTest.kt @@ -1,9 +1,7 @@ package space.kscience.visionforge.meta -import kotlinx.coroutines.cancel -import kotlinx.coroutines.delay +import kotlinx.coroutines.* import kotlinx.coroutines.flow.collectIndexed -import kotlinx.coroutines.launch import kotlinx.coroutines.test.runTest import space.kscience.dataforge.context.Global import space.kscience.dataforge.context.fetch @@ -20,6 +18,7 @@ private class TestScheme : Scheme() { companion object : SchemeSpec(::TestScheme) } +@OptIn(ExperimentalCoroutinesApi::class) internal class VisionPropertyTest { private val manager = Global.fetch(VisionManager) @@ -68,56 +67,63 @@ internal class VisionPropertyTest { val child = group.children["child"]!! - var value: Value? = null + val deferred: CompletableDeferred = CompletableDeferred() var callCounter = 0 - child.useProperty("test", inherit = true) { + val subscription = child.useProperty("test", inherit = true) { + deferred.complete(it.value) callCounter++ - value = it.value } - assertEquals(22, value?.int) + assertEquals(22, deferred.await()?.int) assertEquals(1, callCounter) child.properties.remove("test") - //Need this to avoid the race - delay(20) - assertEquals(11, child.properties.getProperty("test", inherit = true).int) - assertEquals(11, value?.int) - assertEquals(2, callCounter) +// assertEquals(11, deferred.await()?.int) +// assertEquals(2, callCounter) + subscription.cancel() } @Test fun testChildrenPropertyFlow() = runTest(dispatchTimeoutMs = 200) { val group = Global.fetch(VisionManager).group { + properties { "test" put 11 } + group("child") { properties { "test" put 22 } } + } val child = group.children["child"]!! launch { child.flowPropertyValue("test", inherit = true).collectIndexed { index, value -> - if (index == 0) { - assertEquals(22, value?.int) - } else if (index == 1) { - assertEquals(11, value?.int) - cancel() + when (index) { + 0 -> assertEquals(22, value?.int) + 1 -> assertEquals(11, value?.int) + 2 -> { + assertEquals(33, value?.int) + cancel() + } } } } + //wait for subscription to be created - delay(10) + delay(5) child.properties.remove("test") + + delay(50) + group.properties["test"] = 33 } } \ No newline at end of file diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FX3DPlugin.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FX3DPlugin.kt index d39f284c..8a0abcdb 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FX3DPlugin.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FX3DPlugin.kt @@ -45,7 +45,7 @@ public class FX3DPlugin : AbstractPlugin() { } public fun buildNode(obj: Solid): Node { - val binding = VisualObjectFXBinding(this, obj) + val binding = VisionFXBinding(this, obj) return when (obj) { is SolidReference -> referenceFactory(obj, binding) is SolidGroup -> { @@ -150,7 +150,7 @@ public interface FX3DFactory { public val type: KClass - public operator fun invoke(obj: T, binding: VisualObjectFXBinding): Node + public operator fun invoke(obj: T, binding: VisionFXBinding): Node public companion object { public const val TYPE: String = "fx3DFactory" diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXCompositeFactory.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXCompositeFactory.kt index 588f15cf..2e07527a 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXCompositeFactory.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXCompositeFactory.kt @@ -42,7 +42,7 @@ public class FXCompositeFactory(public val plugin: FX3DPlugin) : FX3DFactory get() = Composite::class - override fun invoke(obj: Composite, binding: VisualObjectFXBinding): Node { + override fun invoke(obj: Composite, binding: VisionFXBinding): Node { val first = plugin.buildNode(obj.first) as? MeshView ?: error("Can't build node") val second = plugin.buildNode(obj.second) as? MeshView ?: error("Can't build node") val firstCSG = first.toCSG() diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXConvexFactory.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXConvexFactory.kt index 7bc44207..ddb342b0 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXConvexFactory.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXConvexFactory.kt @@ -10,7 +10,7 @@ import kotlin.reflect.KClass public object FXConvexFactory : FX3DFactory { override val type: KClass get() = Convex::class - override fun invoke(obj: Convex, binding: VisualObjectFXBinding): Node { + override fun invoke(obj: Convex, binding: VisionFXBinding): Node { val hull = HullUtil.hull( obj.points.map { Vector3d.xyz(it.x.toDouble(), it.y.toDouble(), it.z.toDouble()) }, PropertyStorage() diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXReferenceFactory.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXReferenceFactory.kt index 1c3ecb60..a75771cb 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXReferenceFactory.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXReferenceFactory.kt @@ -14,15 +14,17 @@ import kotlin.reflect.KClass public class FXReferenceFactory(public val plugin: FX3DPlugin) : FX3DFactory { override val type: KClass get() = SolidReference::class - override fun invoke(obj: SolidReference, binding: VisualObjectFXBinding): Node { + override fun invoke(obj: SolidReference, binding: VisionFXBinding): Node { val prototype = obj.prototype val node = plugin.buildNode(prototype) - obj.onPropertyChange { name-> + obj.onPropertyChange { name -> if (name.firstOrNull()?.body == REFERENCE_CHILD_PROPERTY_PREFIX) { - val childName = name.firstOrNull()?.index?.let(Name::parse) ?: error("Wrong syntax for reference child property: '$name'") + val childName = name.firstOrNull()?.index?.let(Name::parse) + ?: error("Wrong syntax for reference child property: '$name'") val propertyName = name.cutFirst() - val referenceChild = obj.children.getChild(childName) ?: error("Reference child with name '$childName' not found") + val referenceChild = + obj.children.getChild(childName) ?: error("Reference child with name '$childName' not found") val child = node.findChild(childName) ?: error("Object child with name '$childName' not found") child.updateProperty(referenceChild, propertyName) } diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXShapeFactory.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXShapeFactory.kt index 116075ce..d4d58db1 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXShapeFactory.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXShapeFactory.kt @@ -11,7 +11,7 @@ import kotlin.reflect.KClass public object FXShapeFactory : FX3DFactory { override val type: KClass get() = GeometrySolid::class - override fun invoke(obj: GeometrySolid, binding: VisualObjectFXBinding): MeshView { + override fun invoke(obj: GeometrySolid, binding: VisionFXBinding): MeshView { val mesh = FXGeometryBuilder().apply { obj.toGeometry(this) }.build() return MeshView(mesh) } diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/VisualObjectFXBinding.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/VisionFXBinding.kt similarity index 96% rename from visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/VisualObjectFXBinding.kt rename to visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/VisionFXBinding.kt index 2eb2255c..cc4f3637 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/VisualObjectFXBinding.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/VisionFXBinding.kt @@ -12,7 +12,7 @@ import tornadofx.* /** * A caching binding collection for [Vision] properties */ -public class VisualObjectFXBinding(public val fx: FX3DPlugin, public val obj: Vision) { +public class VisionFXBinding(public val fx: FX3DPlugin, public val obj: Vision) { private val bindings = HashMap>() init { diff --git a/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/GdmlLoaderOptions.kt b/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/GdmlLoaderOptions.kt index 976db192..34ab7d68 100644 --- a/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/GdmlLoaderOptions.kt +++ b/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/GdmlLoaderOptions.kt @@ -30,7 +30,7 @@ public class GdmlLoaderOptions { styleCache.getOrPut(Name.parse(name)) { Meta(builder) } - useStyle(name) + useStyle(name, false) } public fun Solid.transparent() { diff --git a/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/gdmlLoader.kt b/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/gdmlLoader.kt index f3527148..2e209c5c 100644 --- a/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/gdmlLoader.kt +++ b/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/gdmlLoader.kt @@ -352,7 +352,7 @@ private class GdmlLoader(val settings: GdmlLoaderOptions) { val rootStyle by final.style("gdml") { Solid.ROTATION_ORDER_KEY put RotationOrder.ZXY } - final.useStyle(rootStyle) + final.useStyle(rootStyle, false) final.prototypes { proto.items.forEach { (token, item) -> diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidReference.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidReference.kt index 76eb56aa..1c123632 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidReference.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidReference.kt @@ -1,6 +1,9 @@ package space.kscience.visionforge.solid +import kotlinx.coroutines.DelicateCoroutinesApi +import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.flow.* +import kotlinx.coroutines.launch import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.Transient @@ -65,6 +68,25 @@ public class SolidReference( override fun getValue(name: Name, inherit: Boolean?, includeStyles: Boolean?): Value? { return properties?.getValue(name) ?: prototype.properties.getValue(name, inherit, includeStyles) } + + override fun invalidate(propertyName: Name) { + //send update signal + @OptIn(DelicateCoroutinesApi::class) + (manager?.context ?: GlobalScope).launch { + changesInternal.emit(propertyName) + } + + // update styles + if (propertyName == Vision.STYLE_KEY) { + styles.asSequence() + .mapNotNull { getStyle(it) } + .flatMap { it.items.asSequence() } + .distinctBy { it.key } + .forEach { + invalidate(it.key.asName()) + } + } + } } } @@ -117,11 +139,11 @@ internal class SolidReferenceChild( includeStyles: Boolean?, ): Value? = own.getValue(name) ?: prototype.properties.getValue(name, inherit, includeStyles) - override fun setProperty(name: Name, node: Meta?) { + override fun setProperty(name: Name, node: Meta?, notify: Boolean) { own.setMeta(name, node) } - override fun setValue(name: Name, value: Value?) { + override fun setValue(name: Name, value: Value?, notify: Boolean) { own.setValue(name, value) } diff --git a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/PropertyTest.kt b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/SolidPropertyTest.kt similarity index 92% rename from visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/PropertyTest.kt rename to visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/SolidPropertyTest.kt index 5e26284e..6c9fc288 100644 --- a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/PropertyTest.kt +++ b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/SolidPropertyTest.kt @@ -2,6 +2,7 @@ package space.kscience.visionforge.solid import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.delay import kotlinx.coroutines.test.runTest import space.kscience.dataforge.meta.int import space.kscience.dataforge.meta.string @@ -10,8 +11,9 @@ import space.kscience.visionforge.* import kotlin.test.Test import kotlin.test.assertEquals +@OptIn(ExperimentalCoroutinesApi::class) @Suppress("UNUSED_VARIABLE") -class PropertyTest { +class SolidPropertyTest { @Test fun testColor() { val box = Box(10.0f, 10.0f, 10.0f) @@ -23,7 +25,6 @@ class PropertyTest { assertEquals("pink", box.color.string) } - @OptIn(ExperimentalCoroutinesApi::class) @Test fun testColorUpdate() = runTest(dispatchTimeoutMs = 200) { val box = Box(10.0f, 10.0f, 10.0f) @@ -31,11 +32,12 @@ class PropertyTest { val c = CompletableDeferred() - val subscription = box.onPropertyChange(this) { - if (it == SolidMaterial.MATERIAL_COLOR_KEY) { + val subscription = box.onPropertyChange(this) { key -> + if (key == SolidMaterial.MATERIAL_COLOR_KEY) { c.complete(box.color.string) } } + delay(5) box.material { color.set("pink") diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeLabelFactory.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeLabelFactory.kt index 46df4405..b122f9c3 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeLabelFactory.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeLabelFactory.kt @@ -27,7 +27,7 @@ public object ThreeLabelFactory : ThreeFactory { return Mesh(textGeo, ThreeMaterials.DEFAULT).apply { updateMaterial(obj) updatePosition(obj) - obj.onPropertyChange { _ -> + obj.onPropertyChange { //TODO three.logger.warn { "Label parameter change not implemented" } } diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeMeshFactory.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeMeshFactory.kt index ea39bbea..a35ca12d 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeMeshFactory.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeMeshFactory.kt @@ -43,7 +43,7 @@ public abstract class ThreeMeshFactory( } //add listener to object properties - obj.onPropertyChange { name -> + obj.onPropertyChange { name-> when { name.startsWith(Solid.GEOMETRY_KEY) -> { val oldGeometry = mesh.geometry diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreePlugin.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreePlugin.kt index 7c1358d7..c715efdb 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreePlugin.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreePlugin.kt @@ -11,6 +11,7 @@ import space.kscience.dataforge.meta.update import space.kscience.dataforge.names.* import space.kscience.visionforge.ElementVisionRenderer 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.visible @@ -68,7 +69,7 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer { updatePosition(obj) //obj.onChildrenChange() - obj.properties.changes.onEach { name -> + obj.onPropertyChange(context) { name -> if ( name.startsWith(Solid.POSITION_KEY) || name.startsWith(Solid.ROTATION_KEY) || @@ -79,7 +80,7 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer { } else if (name == Vision.VISIBLE_KEY) { visible = obj.visible ?: true } - }.launchIn(context) + } obj.children.changes.onEach { childName -> val child = obj.children.getChild(childName) @@ -101,6 +102,7 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer { }.launchIn(context) } } + is Composite -> compositeFactory.build(this, obj) else -> { //find specialized factory for this type if it is present @@ -179,6 +181,7 @@ internal fun Object3D.getOrCreateGroup(name: Name): Object3D { this.add(group) } } + else -> getOrCreateGroup(name.tokens.first().asName()).getOrCreateGroup(name.cutFirst()) } }