diff --git a/demo/gdml/src/commonTest/kotlin/space/kscience/visionforge/gdml/GDMLVisualTest.kt b/demo/gdml/src/commonTest/kotlin/space/kscience/visionforge/gdml/GDMLVisionTest.kt similarity index 97% rename from demo/gdml/src/commonTest/kotlin/space/kscience/visionforge/gdml/GDMLVisualTest.kt rename to demo/gdml/src/commonTest/kotlin/space/kscience/visionforge/gdml/GDMLVisionTest.kt index c12c7215..17bdc014 100644 --- a/demo/gdml/src/commonTest/kotlin/space/kscience/visionforge/gdml/GDMLVisualTest.kt +++ b/demo/gdml/src/commonTest/kotlin/space/kscience/visionforge/gdml/GDMLVisionTest.kt @@ -10,7 +10,7 @@ import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNotNull -class GDMLVisualTest { +class GDMLVisionTest { // @Test // fun testCubesStyles(){ diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionBase.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionBase.kt index c9b68219..62f90f52 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionBase.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionBase.kt @@ -28,11 +28,10 @@ internal data class PropertyListener( */ @Serializable @SerialName("vision") -public open class VisionBase : Vision { +public open class VisionBase( + override @Transient var parent: VisionGroup? = null, protected var properties: Config? = null - - @Transient - override var parent: VisionGroup? = null +) : Vision { @Synchronized protected fun getOrCreateProperties(): Config { diff --git a/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/layerMarker.kt b/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/layerMarker.kt new file mode 100644 index 00000000..17b75e79 --- /dev/null +++ b/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/layerMarker.kt @@ -0,0 +1,23 @@ +package space.kscience.visionforge.gdml + +import space.kscience.dataforge.names.Name +import space.kscience.dataforge.names.plus +import space.kscience.visionforge.VisionGroup + +private fun VisionGroup.countChildren(namePrefix: Name, cache: MutableMap = hashMapOf()): Int { + var counter = 0 + children.forEach { (token, child) -> + if (child is VisionGroup) { + counter += child.countChildren(namePrefix + token, cache) + } else { + counter++ + } + } + cache[namePrefix] = counter + return counter +} + + +public fun VisionGroup.processLayers() { + +} \ No newline at end of file diff --git a/visionforge-gdml/src/commonTest/kotlin/TestCubes.kt b/visionforge-gdml/src/commonTest/kotlin/TestCubes.kt index a364996c..9fb80095 100644 --- a/visionforge-gdml/src/commonTest/kotlin/TestCubes.kt +++ b/visionforge-gdml/src/commonTest/kotlin/TestCubes.kt @@ -26,7 +26,7 @@ class TestCubes { val smallBoxPrototype = vision.getPrototype("solids.smallBox".toName()) as? Box assertNotNull(smallBoxPrototype) assertEquals(30.0, smallBoxPrototype.xSize.toDouble()) - val smallBoxVision = vision["composite-111.smallBox"]?.prototype as? Box + val smallBoxVision = vision["composite-111.smallBox"]?.unref as? Box assertNotNull(smallBoxVision) assertEquals(30.0, smallBoxVision.xSize.toDouble()) } diff --git a/visionforge-gdml/src/jvmTest/kotlin/space/kscience/visionforge/gdml/bmanStatistics.kt b/visionforge-gdml/src/jvmTest/kotlin/space/kscience/visionforge/gdml/bmanStatistics.kt index eccac343..1f1da074 100644 --- a/visionforge-gdml/src/jvmTest/kotlin/space/kscience/visionforge/gdml/bmanStatistics.kt +++ b/visionforge-gdml/src/jvmTest/kotlin/space/kscience/visionforge/gdml/bmanStatistics.kt @@ -5,7 +5,6 @@ import kotlinx.coroutines.withContext import nl.adaptivity.xmlutil.StAXReader import space.kscience.gdml.Gdml import space.kscience.gdml.decodeFromReader -import space.kscience.visionforge.solid.prototype import space.kscience.visionforge.visitor.countDistinct import space.kscience.visionforge.visitor.flowStatistics import java.io.File @@ -23,7 +22,7 @@ suspend fun main() { vision.flowStatistics>{ _, child -> - child.prototype::class + child::class }.countDistinct().forEach { (depth, size) -> println("$depth\t$size") } diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Solid.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Solid.kt index 0b3dee57..a4c8e69c 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Solid.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Solid.kt @@ -34,6 +34,7 @@ import kotlin.reflect.KProperty * Interface for 3-dimensional [Vision] */ public interface Solid : Vision { + override val descriptor: NodeDescriptor get() = Companion.descriptor public companion object { diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidBase.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidBase.kt index 1f86bdb3..c88052b2 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidBase.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidBase.kt @@ -12,11 +12,7 @@ import space.kscience.visionforge.VisionChange @Serializable @SerialName("solid") -public open class SolidBase( -// override var position: Point3D? = null, -// override var rotation: Point3D? = null, -// override var scale: Point3D? = null, -) : VisionBase(), Solid { +public open class SolidBase : VisionBase(), Solid { override val descriptor: NodeDescriptor get() = Solid.descriptor override fun update(change: VisionChange) { diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidGroup.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidGroup.kt index 513a6fc9..27f95c17 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidGroup.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidGroup.kt @@ -1,14 +1,7 @@ package space.kscience.visionforge.solid -import kotlinx.serialization.KSerializer -import kotlinx.serialization.PolymorphicSerializer import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import kotlinx.serialization.builtins.MapSerializer -import kotlinx.serialization.descriptors.SerialDescriptor -import kotlinx.serialization.encoding.Decoder -import kotlinx.serialization.encoding.Encoder -import space.kscience.dataforge.meta.MetaItem import space.kscience.dataforge.meta.descriptors.NodeDescriptor import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.NameToken @@ -27,37 +20,35 @@ public interface PrototypeHolder { */ @Serializable @SerialName("group.solid") -public class SolidGroup( - @Serializable(PrototypeSerializer::class) internal var prototypes: MutableVisionGroup? = null, -) : VisionGroupBase(), Solid, PrototypeHolder { +public class SolidGroup : VisionGroupBase(), Solid, PrototypeHolder { + + override val children: Map get() = super.childrenInternal.filter { it.key != PROTOTYPES_TOKEN } + + public var prototypes: MutableVisionGroup? + get() = childrenInternal[PROTOTYPES_TOKEN] as? MutableVisionGroup + set(value) { + set(PROTOTYPES_TOKEN, value) + } - init { - prototypes?.parent = this - } override val descriptor: NodeDescriptor get() = Solid.descriptor /** - * Ger a prototype redirecting the request to the parent if prototype is not found + * Get a prototype redirecting the request to the parent if prototype is not found. + * If prototype is a ref, then it is unfolded until */ override fun getPrototype(name: Name): Solid? = - (prototypes?.get(name) as? Solid) ?: (parent as? PrototypeHolder)?.getPrototype(name) + prototypes?.get(name)?.unref ?: (parent as? PrototypeHolder)?.getPrototype(name) /** * Create or edit prototype node as a group */ override fun prototypes(builder: VisionContainerBuilder.() -> Unit): Unit { - (prototypes ?: Prototypes().also { + (prototypes ?: SolidGroup().also { prototypes = it - it.parent = this }).run(builder) } -// /** -// * TODO add special static group to hold statics without propagation -// */ -// override fun addStatic(child: VisualObject) = setChild(NameToken("@static(${child.hashCode()})"), child) - override fun createGroup(): SolidGroup = SolidGroup() override fun update(change: VisionChange) { @@ -87,50 +78,3 @@ public fun VisionContainerBuilder.group( @VisionBuilder public fun VisionContainerBuilder.group(name: String, action: SolidGroup.() -> Unit = {}): SolidGroup = SolidGroup().apply(action).also { set(name, it) } - -/** - * A special class which works as a holder for prototypes - */ -internal class Prototypes( - children: MutableMap = hashMapOf(), -) : VisionGroupBase(children), PrototypeHolder { - - override fun getProperty( - name: Name, - inherit: Boolean, - includeStyles: Boolean, - includeDefaults: Boolean, - ): MetaItem? = null - - override fun setProperty(name: Name, item: MetaItem?, notify: Boolean) { - error("Can't set property of a prototypes container") - } - - override val descriptor: NodeDescriptor? = null - - override fun prototypes(builder: VisionContainerBuilder.() -> Unit) { - apply(builder) - } - - override fun getPrototype(name: Name): Solid? = get(name) as? Solid -} - -internal class PrototypeSerializer : KSerializer { - - private val mapSerializer: KSerializer> = - MapSerializer( - NameToken.serializer(), - PolymorphicSerializer(Vision::class) - ) - - override val descriptor: SerialDescriptor get() = mapSerializer.descriptor - - override fun deserialize(decoder: Decoder): MutableVisionGroup { - val map = mapSerializer.deserialize(decoder) - return Prototypes(map.toMutableMap()) - } - - override fun serialize(encoder: Encoder, value: MutableVisionGroup) { - mapSerializer.serialize(encoder, value.children) - } -} \ No newline at end of file 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 8778657f..401f8097 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 @@ -11,10 +11,27 @@ import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.names.* import space.kscience.visionforge.* -public interface SolidReference : Solid { + +public interface SolidReference : VisionGroup { + /** + * The prototype for this reference. Always returns a "real" prototype, not a reference + */ public val prototype: Solid } + +/** + * Get a vision prototype if it is a [SolidReference] or vision itself if it is not. + * Unref is recursive, so it always returns a non-reference. + */ +public val Vision.unref: Solid + get() = when (this) { + is SolidReference -> prototype.unref + is Solid -> this + else -> error("This Vision is neither Solid nor SolidReference") + } + + private fun SolidReference.getRefProperty( name: Name, inherit: Boolean, @@ -31,6 +48,12 @@ private fun SolidReference.getRefProperty( } }.merge() +private fun childToken(childName: Name): NameToken = + NameToken(SolidReferenceGroup.REFERENCE_CHILD_PROPERTY_PREFIX, childName.toString()) + +private fun childPropertyName(childName: Name, propertyName: Name): Name = + childToken(childName) + propertyName + /** * A reference [Solid] to reuse a template object */ @@ -38,48 +61,24 @@ private fun SolidReference.getRefProperty( @SerialName("solid.ref") public class SolidReferenceGroup( public val refName: Name, -) : SolidBase(), SolidReference, VisionGroup { +) : VisionBase(), SolidReference, VisionGroup, Solid { /** * Recursively search for defined template in the parent */ - override val prototype: Solid - get() { - if (parent == null) error("No parent is present for SolidReferenceGroup") - if (parent !is SolidGroup) error("Reference parent is not a group") - return (parent as? SolidGroup)?.getPrototype(refName) - ?: error("Prototype with name $refName not found") - } + override val prototype: Solid by lazy { + if (parent == null) error("No parent is present for SolidReferenceGroup") + if (parent !is PrototypeHolder) error("Parent does not hold prototypes") + (parent as? PrototypeHolder)?.getPrototype(refName) ?: error("Prototype with name $refName not found") + } override val children: Map get() = (prototype as? VisionGroup)?.children - ?.filter { !it.key.toString().startsWith("@") } + ?.filter { it.key != SolidGroup.PROTOTYPES_TOKEN } ?.mapValues { - ReferenceChild(it.key.asName()) + ReferenceChild(this, it.key.asName()) } ?: emptyMap() - private fun childToken(childName: Name): NameToken = - NameToken(REFERENCE_CHILD_PROPERTY_PREFIX, childName.toString()) - - private fun childPropertyName(childName: Name, propertyName: Name): Name = - childToken(childName) + propertyName - - private fun getChildProperty(childName: Name, propertyName: Name): MetaItem? { - return getOwnProperty(childPropertyName(childName, propertyName)) - } - - private fun setChildProperty(childName: Name, propertyName: Name, item: MetaItem?, notify: Boolean) { - setProperty(childPropertyName(childName, propertyName), item, notify) - } - - private fun prototypeFor(name: Name): Solid { - return if (name.isEmpty()) prototype else { - val proto = (prototype as? SolidGroup)?.get(name) - ?: error("Prototype with name $name not found in SolidReferenceGroup $refName") - proto as? Solid ?: error("Prototype with name $name is ${proto::class} but expected Solid") - } - } - override fun getProperty( name: Name, inherit: Boolean, @@ -94,20 +93,31 @@ public class SolidReferenceGroup( * A ProxyChild is created temporarily only to interact with properties, it does not store any values * (properties are stored in external cache) and created and destroyed on-demand). */ - private inner class ReferenceChild(private val childName: Name) : SolidReference, VisionGroup { - override val prototype: Solid get() = prototypeFor(childName) + private class ReferenceChild( + val owner: SolidReferenceGroup, + private val childName: Name + ) : SolidReference, VisionGroup { + + override val prototype: Solid by lazy { + if (childName.isEmpty()) owner.prototype else { + val proto = (owner.prototype as? VisionGroup)?.get(childName) + ?: error("Prototype with name $childName not found in SolidReferenceGroup ${owner.refName}") + proto.unref as? Solid ?: error("Prototype with name $childName is ${proto::class} but expected Solid") + } + } override val children: Map get() = (prototype as? VisionGroup)?.children - ?.filter { !it.key.toString().startsWith("@") } + ?.filter { it.key != SolidGroup.PROTOTYPES_TOKEN } ?.mapValues { (key, _) -> - ReferenceChild(childName + key.asName()) + ReferenceChild(owner, childName + key.asName()) } ?: emptyMap() - override fun getOwnProperty(name: Name): MetaItem? = getChildProperty(childName, name) + override fun getOwnProperty(name: Name): MetaItem? = + owner.getOwnProperty(childPropertyName(childName, name)) override fun setProperty(name: Name, item: MetaItem?, notify: Boolean) { - setChildProperty(childName, name, item, notify) + owner.setProperty(childPropertyName(childName, name), item, notify) } override fun getProperty( @@ -124,7 +134,7 @@ public class SolidReferenceGroup( override var parent: VisionGroup? get() { val parentName = childName.cutLast() - return if (parentName.isEmpty()) this@SolidReferenceGroup else ReferenceChild(parentName) + return if (parentName.isEmpty()) owner else ReferenceChild(owner, parentName) } set(_) { error("Setting a parent for a reference child is not possible") @@ -132,7 +142,7 @@ public class SolidReferenceGroup( @DFExperimental override val propertyChanges: Flow - get() = this@SolidReferenceGroup.propertyChanges.mapNotNull { name -> + get() = owner.propertyChanges.mapNotNull { name -> if (name.startsWith(childToken(childName))) { name.cutFirst() } else { @@ -141,7 +151,7 @@ public class SolidReferenceGroup( } override fun invalidateProperty(propertyName: Name) { - this@SolidReferenceGroup.invalidateProperty(childPropertyName(childName, propertyName)) + owner.invalidateProperty(childPropertyName(childName, propertyName)) } override fun update(change: VisionChange) { @@ -159,12 +169,6 @@ public class SolidReferenceGroup( } } -/** - * Get a vision prototype if it is a [SolidReferenceGroup] or vision itself if it is not - */ -public val Vision.prototype: Vision - get() = if (this is SolidReference) prototype.prototype else this - /** * Create ref for existing prototype */