diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/AbstractVisualGroup.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/AbstractVisualGroup.kt index aa31387b..d130b5c7 100644 --- a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/AbstractVisualGroup.kt +++ b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/AbstractVisualGroup.kt @@ -11,8 +11,7 @@ import kotlinx.serialization.Transient /** * Abstract implementation of mutable group of [VisualObject] */ -abstract class AbstractVisualGroup : AbstractVisualObject(), - MutableVisualGroup { +abstract class AbstractVisualGroup : AbstractVisualObject(), MutableVisualGroup { //protected abstract val _children: MutableMap @@ -21,6 +20,17 @@ abstract class AbstractVisualGroup : AbstractVisualObject(), */ abstract override val children: Map + abstract override var styleSheet: StyleSheet? + protected set + + /** + * Update or create stylesheet + */ + fun styleSheet(block: StyleSheet.() -> Unit) { + val res = styleSheet ?: StyleSheet(this).also { styleSheet = it } + res.block() + } + override fun propertyChanged(name: Name, before: MetaItem<*>?, after: MetaItem<*>?) { super.propertyChanged(name, before, after) forEach { diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/SimpleVisualGroup.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/SimpleVisualGroup.kt new file mode 100644 index 00000000..468a5264 --- /dev/null +++ b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/SimpleVisualGroup.kt @@ -0,0 +1,31 @@ +package hep.dataforge.vis + +import hep.dataforge.meta.Config +import hep.dataforge.names.NameToken +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + + +@Serializable +@SerialName("group") +class SimpleVisualGroup : AbstractVisualGroup() { + + override var styleSheet: StyleSheet? = null + + //FIXME to be lifted to AbstractVisualGroup after https://github.com/Kotlin/kotlinx.serialization/issues/378 is fixed + override var properties: Config? = null + + @SerialName("children") + private val _children = HashMap() + override val children: Map get() = _children + + override fun removeChild(token: NameToken) { + _children.remove(token)?.apply { parent = null } + } + + override fun setChild(token: NameToken, child: VisualObject) { + _children[token] = child + } + + override fun createGroup(): SimpleVisualGroup = SimpleVisualGroup() +} \ No newline at end of file diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/VisualObject.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/VisualObject.kt index e5028b21..b757fcb3 100644 --- a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/VisualObject.kt +++ b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/VisualObject.kt @@ -9,6 +9,7 @@ import hep.dataforge.names.asName import hep.dataforge.names.toName import hep.dataforge.provider.Type import hep.dataforge.vis.VisualObject.Companion.TYPE +import kotlinx.serialization.PolymorphicSerializer import kotlinx.serialization.Transient //private fun Laminate.withTop(meta: Meta): Laminate = Laminate(listOf(meta) + layers) @@ -64,6 +65,10 @@ interface VisualObject : Configurable { const val TYPE = "visual" val STYLE_KEY = "@style".asName() + private val VISUAL_OBJECT_SERIALIZER = PolymorphicSerializer(VisualObject::class) + + fun serializer() = VISUAL_OBJECT_SERIALIZER + //const val META_KEY = "@meta" //const val TAGS_KEY = "@tags" diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Proxy.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Proxy.kt index 8dca1fd1..b60f8d42 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Proxy.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Proxy.kt @@ -154,11 +154,10 @@ val VisualObject.prototype: VisualObject /** * Create ref for existing prototype */ -inline fun VisualGroup3D.ref( +fun VisualGroup3D.ref( templateName: Name, - name: String = "", - block: Proxy.() -> Unit = {} -) = Proxy(this, templateName).apply(block).also { set(name, it) } + name: String = "" +): Proxy = Proxy(this, templateName).also { set(name, it) } /** * Add new proxy wrapping given object and automatically adding it to the prototypes @@ -166,8 +165,7 @@ inline fun VisualGroup3D.ref( fun VisualGroup3D.proxy( name: String, obj: VisualObject3D, - templateName: Name = name.toName(), - block: Proxy.() -> Unit = {} + templateName: Name = name.toName() ): Proxy { val existing = getPrototype(templateName) if (existing == null) { @@ -177,5 +175,14 @@ fun VisualGroup3D.proxy( } else if (existing != obj) { error("Can't add different prototype on top of existing one") } - return ref(templateName, name, block) + return ref(templateName, name) +} + +fun VisualGroup3D.proxyGroup( + name: String, + templateName: Name = name.toName(), + block: MutableVisualGroup.() -> Unit +): Proxy { + val group = VisualGroup3D().apply(block) + return proxy(name, group, templateName) } diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Visual3D.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Visual3D.kt index 26cb67c5..5e13b385 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Visual3D.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Visual3D.kt @@ -7,6 +7,7 @@ import hep.dataforge.context.PluginTag import hep.dataforge.meta.* import hep.dataforge.names.Name import hep.dataforge.names.toName +import hep.dataforge.vis.SimpleVisualGroup import hep.dataforge.vis.Visual import hep.dataforge.vis.VisualObject import kotlinx.serialization.json.Json @@ -38,23 +39,26 @@ class Visual3D(meta: Meta) : AbstractPlugin(meta) { contextual(Point2DSerializer) polymorphic(VisualObject::class, VisualObject3D::class) { - VisualGroup3D::class with VisualGroup3D.serializer() - Proxy::class with Proxy.serializer() - Composite::class with Composite.serializer() - Tube::class with Tube.serializer() - Box::class with Box.serializer() - Convex::class with Convex.serializer() - Extruded::class with Extruded.serializer() + subclass(SimpleVisualGroup.serializer()) + subclass(VisualGroup3D.serializer()) + subclass(Proxy.serializer()) + subclass(Composite.serializer()) + subclass(Tube.serializer()) + subclass(Box.serializer()) + subclass(Convex.serializer()) + subclass(Extruded.serializer()) subclass(PolyLine.serializer()) subclass(Label3D.serializer()) + subclass(Sphere.serializer()) } } - val json = Json( + internal val json = Json( JsonConfiguration( prettyPrint = true, useArrayPolymorphism = false, - encodeDefaults = false + encodeDefaults = false, + ignoreUnknownKeys = true ), context = serialModule ) diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/VisualGroup3D.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/VisualGroup3D.kt index a9162916..d7709464 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/VisualGroup3D.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/VisualGroup3D.kt @@ -27,7 +27,6 @@ interface PrototypeHolder { class VisualGroup3D : AbstractVisualGroup(), VisualObject3D, PrototypeHolder { override var styleSheet: StyleSheet? = null - private set /** * A container for templates visible inside this group @@ -63,14 +62,6 @@ class VisualGroup3D : AbstractVisualGroup(), VisualObject3D, PrototypeHolder { super.attachChildren() } - /** - * Update or create stylesheet - */ - fun styleSheet(block: StyleSheet.() -> Unit) { - val res = styleSheet ?: StyleSheet(this).also { styleSheet = it } - res.block() - } - override fun removeChild(token: NameToken) { _children.remove(token)?.apply { parent = null } } @@ -86,20 +77,6 @@ class VisualGroup3D : AbstractVisualGroup(), VisualObject3D, PrototypeHolder { override fun createGroup(): VisualGroup3D = VisualGroup3D() -// return when { -// name.isEmpty() -> error("Should be unreachable") -// name.length == 1 -> { -// val token = name.first()!! -// when (val current = children[token]) { -// null -> VisualGroup3D().also { setChild(token, it) } -// is VisualGroup3D -> current -// else -> error("Can't create group with name $name because it exists and not a group") -// } -// } -// else -> createGroup(name.first()!!.asName()).createGroup(name.cutFirst()) -// } -// } - companion object { // val PROTOTYPES_KEY = NameToken("@prototypes") @@ -109,8 +86,6 @@ class VisualGroup3D : AbstractVisualGroup(), VisualObject3D, PrototypeHolder { } } -fun VisualGroup3D.stringify(): String = Visual3D.json.stringify(VisualGroup3D.serializer(),this) - /** * Ger a prototype redirecting the request to the parent if prototype is not found */ @@ -129,7 +104,11 @@ internal class Prototypes( override var children: MutableMap = LinkedHashMap() ) : AbstractVisualGroup(), MutableVisualGroup, PrototypeHolder { - override val styleSheet: StyleSheet? get() = null + override var styleSheet: StyleSheet? + get() = null + set(value) { + error("Can't define stylesheet for prototypes block") + } override fun removeChild(token: NameToken) { children.remove(token) @@ -140,9 +119,13 @@ internal class Prototypes( children[token] = child } - override fun createGroup(): Prototypes = Prototypes() + override fun createGroup() = SimpleVisualGroup() - override var properties: Config? = null + override var properties: Config? + get() = null + set(value) { + error("Can't define properties for prototypes block") + } override val prototypes: MutableVisualGroup get() = this diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/serialization.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/serialization.kt index 599c4136..d4164529 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/serialization.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/serialization.kt @@ -3,6 +3,7 @@ package hep.dataforge.vis.spatial import hep.dataforge.meta.double import hep.dataforge.names.NameToken import hep.dataforge.vis.MutableVisualGroup +import hep.dataforge.vis.VisualGroup import hep.dataforge.vis.VisualObject import kotlinx.serialization.* import kotlinx.serialization.builtins.MapSerializer @@ -98,11 +99,13 @@ object Point2DSerializer : KSerializer { @Serializer(MutableVisualGroup::class) internal object PrototypesSerializer : KSerializer { + private val mapSerializer: KSerializer> = MapSerializer( NameToken.serializer(), - PolymorphicSerializer(VisualObject::class) + VisualObject.serializer() ) + override val descriptor: SerialDescriptor get() = mapSerializer.descriptor override fun deserialize(decoder: Decoder): MutableVisualGroup { @@ -113,6 +116,12 @@ internal object PrototypesSerializer : KSerializer { override fun serialize(encoder: Encoder, value: MutableVisualGroup) { mapSerializer.serialize(encoder, value.children) } +} +fun VisualObject.stringify(): String = Visual3D.json.stringify(VisualObject.serializer(), this) +fun VisualObject.Companion.parseJson(str: String) = Visual3D.json.parse(VisualObject.serializer(), str).also { + if(it is VisualGroup){ + it.attachChildren() + } } \ No newline at end of file diff --git a/dataforge-vis-spatial/src/commonTest/kotlin/hep/dataforge/vis/spatial/SerializationTest.kt b/dataforge-vis-spatial/src/commonTest/kotlin/hep/dataforge/vis/spatial/SerializationTest.kt index ff542650..6dd4cd5a 100644 --- a/dataforge-vis-spatial/src/commonTest/kotlin/hep/dataforge/vis/spatial/SerializationTest.kt +++ b/dataforge-vis-spatial/src/commonTest/kotlin/hep/dataforge/vis/spatial/SerializationTest.kt @@ -1,7 +1,8 @@ package hep.dataforge.vis.spatial +import hep.dataforge.names.toName +import hep.dataforge.vis.VisualObject import hep.dataforge.vis.get -import hep.dataforge.vis.spatial.Visual3D.Companion.json import kotlin.test.Test import kotlin.test.assertEquals @@ -13,9 +14,9 @@ class SerializationTest { x = 100 z = -100 } - val string = json.stringify(Box.serializer(), cube) + val string = cube.stringify() println(string) - val newCube = json.parse(Box.serializer(), string) + val newCube = VisualObject.parseJson(string) assertEquals(cube.config, newCube.config) } @@ -26,9 +27,13 @@ class SerializationTest { x = 100 z = -100 } - val group = VisualGroup3D().apply { proxy("cube", cube) + proxyGroup("pg", "pg.content".toName()){ + sphere(50){ + x = -100 + } + } } val string = group.stringify() println(string)