v0.2.0-dev-22 #47
@ -10,7 +10,7 @@ import kotlin.test.Test
|
|||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertNotNull
|
import kotlin.test.assertNotNull
|
||||||
|
|
||||||
class GDMLVisualTest {
|
class GDMLVisionTest {
|
||||||
|
|
||||||
// @Test
|
// @Test
|
||||||
// fun testCubesStyles(){
|
// fun testCubesStyles(){
|
@ -28,11 +28,10 @@ internal data class PropertyListener(
|
|||||||
*/
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("vision")
|
@SerialName("vision")
|
||||||
public open class VisionBase : Vision {
|
public open class VisionBase(
|
||||||
|
override @Transient var parent: VisionGroup? = null,
|
||||||
protected var properties: Config? = null
|
protected var properties: Config? = null
|
||||||
|
) : Vision {
|
||||||
@Transient
|
|
||||||
override var parent: VisionGroup? = null
|
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
protected fun getOrCreateProperties(): Config {
|
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
|
val smallBoxPrototype = vision.getPrototype("solids.smallBox".toName()) as? Box
|
||||||
assertNotNull(smallBoxPrototype)
|
assertNotNull(smallBoxPrototype)
|
||||||
assertEquals(30.0, smallBoxPrototype.xSize.toDouble())
|
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)
|
assertNotNull(smallBoxVision)
|
||||||
assertEquals(30.0, smallBoxVision.xSize.toDouble())
|
assertEquals(30.0, smallBoxVision.xSize.toDouble())
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@ import kotlinx.coroutines.withContext
|
|||||||
import nl.adaptivity.xmlutil.StAXReader
|
import nl.adaptivity.xmlutil.StAXReader
|
||||||
import space.kscience.gdml.Gdml
|
import space.kscience.gdml.Gdml
|
||||||
import space.kscience.gdml.decodeFromReader
|
import space.kscience.gdml.decodeFromReader
|
||||||
import space.kscience.visionforge.solid.prototype
|
|
||||||
import space.kscience.visionforge.visitor.countDistinct
|
import space.kscience.visionforge.visitor.countDistinct
|
||||||
import space.kscience.visionforge.visitor.flowStatistics
|
import space.kscience.visionforge.visitor.flowStatistics
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@ -23,7 +22,7 @@ suspend fun main() {
|
|||||||
|
|
||||||
|
|
||||||
vision.flowStatistics<KClass<*>>{ _, child ->
|
vision.flowStatistics<KClass<*>>{ _, child ->
|
||||||
child.prototype::class
|
child::class
|
||||||
}.countDistinct().forEach { (depth, size) ->
|
}.countDistinct().forEach { (depth, size) ->
|
||||||
println("$depth\t$size")
|
println("$depth\t$size")
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,7 @@ import kotlin.reflect.KProperty
|
|||||||
* Interface for 3-dimensional [Vision]
|
* Interface for 3-dimensional [Vision]
|
||||||
*/
|
*/
|
||||||
public interface Solid : Vision {
|
public interface Solid : Vision {
|
||||||
|
|
||||||
override val descriptor: NodeDescriptor get() = Companion.descriptor
|
override val descriptor: NodeDescriptor get() = Companion.descriptor
|
||||||
|
|
||||||
public companion object {
|
public companion object {
|
||||||
|
@ -12,11 +12,7 @@ import space.kscience.visionforge.VisionChange
|
|||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("solid")
|
@SerialName("solid")
|
||||||
public open class SolidBase(
|
public open class SolidBase : VisionBase(), Solid {
|
||||||
// override var position: Point3D? = null,
|
|
||||||
// override var rotation: Point3D? = null,
|
|
||||||
// override var scale: Point3D? = null,
|
|
||||||
) : VisionBase(), Solid {
|
|
||||||
override val descriptor: NodeDescriptor get() = Solid.descriptor
|
override val descriptor: NodeDescriptor get() = Solid.descriptor
|
||||||
|
|
||||||
override fun update(change: VisionChange) {
|
override fun update(change: VisionChange) {
|
||||||
|
@ -1,14 +1,7 @@
|
|||||||
package space.kscience.visionforge.solid
|
package space.kscience.visionforge.solid
|
||||||
|
|
||||||
import kotlinx.serialization.KSerializer
|
|
||||||
import kotlinx.serialization.PolymorphicSerializer
|
|
||||||
import kotlinx.serialization.SerialName
|
import kotlinx.serialization.SerialName
|
||||||
import kotlinx.serialization.Serializable
|
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.meta.descriptors.NodeDescriptor
|
||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.Name
|
||||||
import space.kscience.dataforge.names.NameToken
|
import space.kscience.dataforge.names.NameToken
|
||||||
@ -27,37 +20,35 @@ public interface PrototypeHolder {
|
|||||||
*/
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("group.solid")
|
@SerialName("group.solid")
|
||||||
public class SolidGroup(
|
public class SolidGroup : VisionGroupBase(), Solid, PrototypeHolder {
|
||||||
@Serializable(PrototypeSerializer::class) internal var prototypes: MutableVisionGroup? = null,
|
|
||||||
) : 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
|
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? =
|
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
|
* Create or edit prototype node as a group
|
||||||
*/
|
*/
|
||||||
override fun prototypes(builder: VisionContainerBuilder<Solid>.() -> Unit): Unit {
|
override fun prototypes(builder: VisionContainerBuilder<Solid>.() -> Unit): Unit {
|
||||||
(prototypes ?: Prototypes().also {
|
(prototypes ?: SolidGroup().also {
|
||||||
prototypes = it
|
prototypes = it
|
||||||
it.parent = this
|
|
||||||
}).run(builder)
|
}).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 createGroup(): SolidGroup = SolidGroup()
|
||||||
|
|
||||||
override fun update(change: VisionChange) {
|
override fun update(change: VisionChange) {
|
||||||
@ -87,50 +78,3 @@ public fun VisionContainerBuilder<Vision>.group(
|
|||||||
@VisionBuilder
|
@VisionBuilder
|
||||||
public fun VisionContainerBuilder<Vision>.group(name: String, action: SolidGroup.() -> Unit = {}): SolidGroup =
|
public fun VisionContainerBuilder<Vision>.group(name: String, action: SolidGroup.() -> Unit = {}): SolidGroup =
|
||||||
SolidGroup().apply(action).also { set(name, it) }
|
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.dataforge.names.*
|
||||||
import space.kscience.visionforge.*
|
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
|
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(
|
private fun SolidReference.getRefProperty(
|
||||||
name: Name,
|
name: Name,
|
||||||
inherit: Boolean,
|
inherit: Boolean,
|
||||||
@ -31,6 +48,12 @@ private fun SolidReference.getRefProperty(
|
|||||||
}
|
}
|
||||||
}.merge()
|
}.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
|
* A reference [Solid] to reuse a template object
|
||||||
*/
|
*/
|
||||||
@ -38,48 +61,24 @@ private fun SolidReference.getRefProperty(
|
|||||||
@SerialName("solid.ref")
|
@SerialName("solid.ref")
|
||||||
public class SolidReferenceGroup(
|
public class SolidReferenceGroup(
|
||||||
public val refName: Name,
|
public val refName: Name,
|
||||||
) : SolidBase(), SolidReference, VisionGroup {
|
) : VisionBase(), SolidReference, VisionGroup, Solid {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recursively search for defined template in the parent
|
* Recursively search for defined template in the parent
|
||||||
*/
|
*/
|
||||||
override val prototype: Solid
|
override val prototype: Solid by lazy {
|
||||||
get() {
|
if (parent == null) error("No parent is present for SolidReferenceGroup")
|
||||||
if (parent == null) error("No parent is present for SolidReferenceGroup")
|
if (parent !is PrototypeHolder) error("Parent does not hold prototypes")
|
||||||
if (parent !is SolidGroup) error("Reference parent is not a group")
|
(parent as? PrototypeHolder)?.getPrototype(refName) ?: error("Prototype with name $refName not found")
|
||||||
return (parent as? SolidGroup)?.getPrototype(refName)
|
}
|
||||||
?: error("Prototype with name $refName not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
override val children: Map<NameToken, Vision>
|
override val children: Map<NameToken, Vision>
|
||||||
get() = (prototype as? VisionGroup)?.children
|
get() = (prototype as? VisionGroup)?.children
|
||||||
?.filter { !it.key.toString().startsWith("@") }
|
?.filter { it.key != SolidGroup.PROTOTYPES_TOKEN }
|
||||||
?.mapValues {
|
?.mapValues {
|
||||||
ReferenceChild(it.key.asName())
|
ReferenceChild(this, it.key.asName())
|
||||||
} ?: emptyMap()
|
} ?: 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(
|
override fun getProperty(
|
||||||
name: Name,
|
name: Name,
|
||||||
inherit: Boolean,
|
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
|
* 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).
|
* (properties are stored in external cache) and created and destroyed on-demand).
|
||||||
*/
|
*/
|
||||||
private inner class ReferenceChild(private val childName: Name) : SolidReference, VisionGroup {
|
private class ReferenceChild(
|
||||||
override val prototype: Solid get() = prototypeFor(childName)
|
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>
|
override val children: Map<NameToken, Vision>
|
||||||
get() = (prototype as? VisionGroup)?.children
|
get() = (prototype as? VisionGroup)?.children
|
||||||
?.filter { !it.key.toString().startsWith("@") }
|
?.filter { it.key != SolidGroup.PROTOTYPES_TOKEN }
|
||||||
?.mapValues { (key, _) ->
|
?.mapValues { (key, _) ->
|
||||||
ReferenceChild(childName + key.asName())
|
ReferenceChild(owner, childName + key.asName())
|
||||||
} ?: emptyMap()
|
} ?: 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) {
|
override fun setProperty(name: Name, item: MetaItem?, notify: Boolean) {
|
||||||
setChildProperty(childName, name, item, notify)
|
owner.setProperty(childPropertyName(childName, name), item, notify)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getProperty(
|
override fun getProperty(
|
||||||
@ -124,7 +134,7 @@ public class SolidReferenceGroup(
|
|||||||
override var parent: VisionGroup?
|
override var parent: VisionGroup?
|
||||||
get() {
|
get() {
|
||||||
val parentName = childName.cutLast()
|
val parentName = childName.cutLast()
|
||||||
return if (parentName.isEmpty()) this@SolidReferenceGroup else ReferenceChild(parentName)
|
return if (parentName.isEmpty()) owner else ReferenceChild(owner, parentName)
|
||||||
}
|
}
|
||||||
set(_) {
|
set(_) {
|
||||||
error("Setting a parent for a reference child is not possible")
|
error("Setting a parent for a reference child is not possible")
|
||||||
@ -132,7 +142,7 @@ public class SolidReferenceGroup(
|
|||||||
|
|
||||||
@DFExperimental
|
@DFExperimental
|
||||||
override val propertyChanges: Flow<Name>
|
override val propertyChanges: Flow<Name>
|
||||||
get() = this@SolidReferenceGroup.propertyChanges.mapNotNull { name ->
|
get() = owner.propertyChanges.mapNotNull { name ->
|
||||||
if (name.startsWith(childToken(childName))) {
|
if (name.startsWith(childToken(childName))) {
|
||||||
name.cutFirst()
|
name.cutFirst()
|
||||||
} else {
|
} else {
|
||||||
@ -141,7 +151,7 @@ public class SolidReferenceGroup(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun invalidateProperty(propertyName: Name) {
|
override fun invalidateProperty(propertyName: Name) {
|
||||||
this@SolidReferenceGroup.invalidateProperty(childPropertyName(childName, propertyName))
|
owner.invalidateProperty(childPropertyName(childName, propertyName))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun update(change: VisionChange) {
|
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
|
* Create ref for existing prototype
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user