v0.2.0-dev-22 #47
@ -10,7 +10,7 @@ import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotNull
|
||||
|
||||
class GDMLVisualTest {
|
||||
class GDMLVisionTest {
|
||||
|
||||
// @Test
|
||||
// fun testCubesStyles(){
|
@ -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 {
|
||||
|
@ -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<Name, Int> = 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() {
|
||||
|
||||
}
|
@ -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())
|
||||
}
|
||||
|
@ -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<KClass<*>>{ _, child ->
|
||||
child.prototype::class
|
||||
child::class
|
||||
}.countDistinct().forEach { (depth, size) ->
|
||||
println("$depth\t$size")
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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) {
|
||||
|
@ -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<NameToken, Vision> 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<Solid>.() -> 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<Vision>.group(
|
||||
@VisionBuilder
|
||||
public fun VisionContainerBuilder<Vision>.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<NameToken, Vision> = 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<Solid>.() -> Unit) {
|
||||
apply(builder)
|
||||
}
|
||||
|
||||
override fun getPrototype(name: Name): Solid? = get(name) as? Solid
|
||||
}
|
||||
|
||||
internal class PrototypeSerializer : KSerializer<MutableVisionGroup> {
|
||||
|
||||
private val mapSerializer: KSerializer<Map<NameToken, Vision>> =
|
||||
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)
|
||||
}
|
||||
}
|
@ -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<NameToken, Vision>
|
||||
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<NameToken, Vision>
|
||||
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<Name>
|
||||
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
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user