diff --git a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/TGeoManager.kt b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/TGeoManager.kt
deleted file mode 100644
index 8b2c3c26..00000000
--- a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/TGeoManager.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-package ru.mipt.npm.root
-
-import kotlinx.serialization.Contextual
-import kotlinx.serialization.SerialName
-import kotlinx.serialization.Serializable
-
-@Serializable
-@SerialName("TGeoManager")
-public class TGeoManager : TNamed() {
-
- @Contextual
- public val fMatrices: TObjArray<@Contextual TGeoMatrix> = TObjArray.getEmpty()
-
- @Contextual
- public val fShapes: TObjArray<@Contextual TGeoShape> = TObjArray.getEmpty()
-
- @Contextual
- public val fVolumes: TObjArray<@Contextual TGeoVolume> = TObjArray.getEmpty()
-
- @Contextual
- public val fNodes: TObjArray<@Contextual TGeoNode> = TObjArray.getEmpty()
-}
diff --git a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/TObjectScheme.kt b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/TObjectScheme.kt
new file mode 100644
index 00000000..0ec699bd
--- /dev/null
+++ b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/TObjectScheme.kt
@@ -0,0 +1,140 @@
+package ru.mipt.npm.root
+
+import kotlinx.serialization.json.Json
+import space.kscience.dataforge.meta.*
+import space.kscience.dataforge.names.Name
+import space.kscience.dataforge.names.asName
+import kotlin.properties.ReadOnlyProperty
+
+public typealias RefCache = List
+
+public interface ObjectRef {
+ public fun resolve(refCache: RefCache): T?
+}
+
+private class ChildObjectRef(
+ val spec: Specification,
+ val metaProvider: () -> Meta?
+) : ObjectRef {
+ override fun resolve(refCache: RefCache): T? {
+ val meta = metaProvider() ?: return null
+ meta["\$ref"]?.int?.let { refId ->
+ return spec.read(refCache[refId])
+ }
+ return spec.read(meta)
+ }
+}
+
+public fun List>.resolve(refCache: RefCache): List = map { it.resolve(refCache)!! }
+
+
+public open class TObjectScheme : Scheme() {
+
+ public val typename: String by string(key = "_typename".asName()) { error("Type is not defined") }
+
+ internal fun tObjectArray(
+ spec: Specification
+ ): ReadOnlyProperty>> = ReadOnlyProperty { _, property ->
+ meta.getIndexed(Name.of(property.name, "arr")).values.map { ChildObjectRef(spec){it} }
+ }
+
+ internal fun refSpec(
+ spec: Specification,
+ key: Name? = null
+ ): ReadOnlyProperty> = ReadOnlyProperty { _, property ->
+ ChildObjectRef(spec) { meta[key ?: property.name.asName()] }
+ }
+
+ public companion object : SchemeSpec(::TObjectScheme)
+}
+
+public open class TNamedScheme : TObjectScheme() {
+ public val fName: String by string("")
+ public val fTitle: String by string("")
+
+ public companion object : SchemeSpec(::TNamedScheme)
+}
+
+public class TGeoMaterialScheme : TNamedScheme() {
+
+ public companion object : SchemeSpec(::TGeoMaterialScheme)
+}
+
+public class TGeoMediumScheme : TNamedScheme() {
+ public val fMaterial: ObjectRef by refSpec(TGeoMaterialScheme)
+ public val fParams: DoubleArray by doubleArray()
+
+ public companion object : SchemeSpec(::TGeoMediumScheme)
+}
+
+public class TGeoShapeScheme : TNamedScheme() {
+ public val fDX: Double by double(0.0)
+ public val fDY: Double by double(0.0)
+ public val fDZ: Double by double(0.0)
+
+ public companion object : SchemeSpec(::TGeoShapeScheme)
+}
+
+public class TGeoVolumeScheme : TNamedScheme() {
+ public val fNodes: List> by tObjectArray(TGeoNodeScheme)
+ public val fShape: ObjectRef by refSpec(TGeoShapeScheme)
+ public val fMedium: ObjectRef by refSpec(TGeoMediumScheme)
+
+ public companion object : SchemeSpec(::TGeoVolumeScheme)
+}
+
+public class TGeoNodeScheme : TNamedScheme() {
+ public val fVolume: ObjectRef by refSpec(TGeoVolumeScheme)
+
+ public companion object : SchemeSpec(::TGeoNodeScheme)
+}
+
+public class TGeoMatrixScheme : TNamedScheme() {
+ public companion object : SchemeSpec(::TGeoMatrixScheme)
+}
+
+
+public class TGeoBoolNodeScheme : TObjectScheme() {
+ public val fLeft: ObjectRef by refSpec(TGeoShapeScheme)
+ public val fLeftMat: ObjectRef by refSpec(TGeoMatrixScheme)
+
+ public val fRight: ObjectRef by refSpec(TGeoShapeScheme)
+ public val fRightMat: ObjectRef by refSpec(TGeoMatrixScheme)
+
+ public companion object : SchemeSpec(::TGeoBoolNodeScheme)
+}
+
+
+public class TGeoManagerScheme : TNamedScheme() {
+ public val fMatrices: List> by tObjectArray(TGeoMatrixScheme)
+
+ public val fShapes: List> by tObjectArray(TGeoShapeScheme)
+
+ public val fVolumes: List> by tObjectArray(TGeoVolumeScheme)
+
+ public val fNodes: List> by tObjectArray(TGeoNodeScheme)
+
+ public val refCache: List by lazy {
+ val res = ArrayList(4096)
+ fun fillCache(element: Meta) {
+ if(element["\$ref"] == null) {
+ res.add(element)
+ element.items.values.forEach {
+ if (!it.isLeaf) {
+ fillCache(it)
+ }
+ }
+ }
+ }
+ fillCache(meta)
+ res
+ }
+
+ public companion object : SchemeSpec(::TGeoManagerScheme) {
+
+ public fun parse(string: String): TGeoManagerScheme {
+ val meta = Json.decodeFromString(MetaSerializer, string)
+ return read(meta)
+ }
+ }
+}
diff --git a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/jsonToRoot.kt b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/jsonToRoot.kt
deleted file mode 100644
index 1b7fe798..00000000
--- a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/jsonToRoot.kt
+++ /dev/null
@@ -1,261 +0,0 @@
-package ru.mipt.npm.root
-
-import kotlinx.serialization.DeserializationStrategy
-import kotlinx.serialization.ExperimentalSerializationApi
-import kotlinx.serialization.KSerializer
-import kotlinx.serialization.descriptors.SerialDescriptor
-import kotlinx.serialization.encoding.Decoder
-import kotlinx.serialization.json.*
-import kotlinx.serialization.modules.*
-
-
-private fun jsonRootDeserializer(tSerializer: KSerializer, builder: (JsonElement) -> T): DeserializationStrategy = object :
- DeserializationStrategy {
- private val jsonElementSerializer = JsonElement.serializer()
-
- override val descriptor: SerialDescriptor
- get() = jsonElementSerializer.descriptor
-
- override fun deserialize(decoder: Decoder): T {
- val json = decoder.decodeSerializableValue(jsonElementSerializer)
- return builder(json)
- }
-}
-
-/**
- * Load Json encoded TObject
- */
-public fun TObject.Companion.decodeFromJson(serializer: KSerializer, jsonElement: JsonElement): T =
- RootDecoder.decode(serializer, jsonElement)
-
-public fun TObject.Companion.decodeFromString(serializer: KSerializer, string: String): T {
- val json = RootDecoder.json.parseToJsonElement(string)
- return RootDecoder.decode(serializer, json)
-}
-
-private object RootDecoder {
-
- private class RootUnrefSerializer(
- private val tSerializer: KSerializer,
- private val refCache: List,
- ) : KSerializer by tSerializer {
-
- override fun deserialize(decoder: Decoder): T {
- val input = decoder as JsonDecoder
- val element = input.decodeJsonElement()
- val refId = (element as? JsonObject)?.get("\$ref")?.jsonPrimitive?.int
- val ref = if (refId != null) {
- //println("Substituting ${tSerializer.descriptor.serialName} ref $refId")
- //Forward ref for shapes
- when (tSerializer.descriptor.serialName) {
- "TGeoShape" -> return TGeoShapeRef {
- refCache[refId].getOrPutValue {
- input.json.decodeFromJsonElement(tSerializer, it) as TGeoShape
- }
- } as T
- "TGeoVolumeAssembly" -> return TGeoVolumeAssemblyRef {
- refCache[refId].getOrPutValue {
- input.json.decodeFromJsonElement(tSerializer, it) as TGeoVolumeAssembly
- }
- } as T
- //Do unref
- else -> refCache[refId]
- }
- } else {
- refCache.find { it.element == element } ?: error("Element '$element' not found in the cache")
- }
-
- return ref.getOrPutValue {
-// val actualTypeName = it.jsonObject["_typename"]?.jsonPrimitive?.content
- input.json.decodeFromJsonElement(tSerializer, it)
- }
- }
- }
-
- private fun KSerializer.unref(refCache: List): KSerializer =
- RootUnrefSerializer(this, refCache)
-
- @OptIn(ExperimentalSerializationApi::class)
- fun unrefSerializersModule(
- refCache: List
- ): SerializersModule = SerializersModule {
- include(serializersModule)
-
- //contextual(TGeoManager.serializer().unref(refCache))
- contextual(TObjArray::class) { TObjArray.serializer(it[0]).unref(refCache) }
- //contextual(TGeoVolumeAssembly.serializer().unref(refCache))
- //contextual(TGeoShapeAssembly.serializer().unref(refCache))
- contextual(TGeoRotation.serializer().unref(refCache))
- contextual(TGeoMedium.serializer().unref(refCache))
- //contextual(TGeoVolume.serializer().unref(refCache))
- //contextual(TGeoMatrix.serializer().unref(refCache))
- //contextual(TGeoNode.serializer().unref(refCache))
- //contextual(TGeoNodeOffset.serializer().unref(refCache))
- //contextual(TGeoNodeMatrix.serializer().unref(refCache))
- //contextual(TGeoShape.serializer().unref(refCache))
- //contextual(TObject.serializer().unref(refCache))
-
-
- polymorphicDefault(TGeoShape::class) {
- if (it == null) {
- TGeoShape.serializer().unref(refCache)
- } else {
- error("Unrecognized shape $it")
- }
- }
-
- polymorphicDefault(TGeoMatrix::class) {
- if (it == null) {
- TGeoMatrix.serializer().unref(refCache)
- } else {
- error("Unrecognized matrix $it")
- }
- }
-
- polymorphicDefault(TGeoVolume::class) {
- if (it == null) {
- TGeoVolume.serializer().unref(refCache)
- } else {
- error("Unrecognized volume $it")
- }
- }
-
- polymorphicDefault(TGeoNode::class) {
- if (it == null) {
- TGeoNode.serializer().unref(refCache)
- } else {
- error("Unrecognized node $it")
- }
- }
- }
-
- /**
- * Create an instance of Json with unfolding Root references. This instance could not be reused because of the cache.
- */
- private fun unrefJson(refCache: MutableList): Json = Json {
- encodeDefaults = true
- ignoreUnknownKeys = true
- classDiscriminator = "_typename"
- serializersModule = unrefSerializersModule(refCache)
- }
-
-
- fun decode(sourceDeserializer: KSerializer, source: JsonElement): T {
- val refCache = ArrayList()
-
- fun fillCache(element: JsonElement) {
- when (element) {
- is JsonObject -> {
- if (element["_typename"] != null) {
- refCache.add(RefEntry(element))
- }
- element.values.forEach {
- fillCache(it)
- }
- }
- is JsonArray -> {
- element.forEach {
- fillCache(it)
- }
- }
- else -> {
- //ignore primitives
- }
- }
- }
- fillCache(source)
-
- return unrefJson(refCache).decodeFromJsonElement(sourceDeserializer.unref(refCache), source)
- }
-
- class RefEntry(val element: JsonElement) {
-
- var value: Any? = null
-
- fun getOrPutValue(builder: (JsonElement) -> T): T {
- if (value == null) {
- value = builder(element)
- }
- return value as T
- }
-
- override fun toString(): String = element.toString()
- }
-
- private fun PolymorphicModuleBuilder.shapes() {
- subclass(TGeoBBox.serializer())
- subclass(TGeoCompositeShape.serializer())
- subclass(TGeoXtru.serializer())
- subclass(TGeoTube.serializer())
- subclass(TGeoTubeSeg.serializer())
- subclass(TGeoPcon.serializer())
- subclass(TGeoPgon.serializer())
- subclass(TGeoShapeAssembly.serializer())
- }
-
- private fun PolymorphicModuleBuilder.matrices() {
- subclass(TGeoIdentity.serializer())
- subclass(TGeoHMatrix.serializer())
- subclass(TGeoTranslation.serializer())
- subclass(TGeoRotation.serializer())
- subclass(TGeoCombiTrans.serializer())
- }
-
- private fun PolymorphicModuleBuilder.boolNodes() {
- subclass(TGeoIntersection.serializer())
- subclass(TGeoUnion.serializer())
- subclass(TGeoSubtraction.serializer())
- }
-
- private val serializersModule = SerializersModule {
-
-// polymorphic(TObject::class) {
-// default { JsonRootSerializer }
-//
-// shapes()
-// matrices()
-// boolNodes()
-//
-// subclass(TGeoMaterial.serializer())
-// subclass(TGeoMixture.serializer())
-//
-// subclass(TGeoMedium.serializer())
-//
-// //subclass(TGeoNode.serializer())
-// subclass(TGeoNodeMatrix.serializer())
-// subclass(TGeoVolume.serializer())
-// subclass(TGeoVolumeAssembly.serializer())
-// subclass(TGeoManager.serializer())
-// }
-
- polymorphic(TGeoShape::class) {
- shapes()
- }
-
- polymorphic(TGeoMatrix::class) {
- matrices()
- }
-
- polymorphic(TGeoBoolNode::class) {
- boolNodes()
- }
-
- polymorphic(TGeoNode::class) {
- subclass(TGeoNodeMatrix.serializer())
- subclass(TGeoNodeOffset.serializer())
- }
-
- polymorphic(TGeoVolume::class) {
- subclass(TGeoVolume.serializer())
- subclass(TGeoVolumeAssembly.serializer())
- }
- }
-
- val json = Json {
- encodeDefaults = true
- ignoreUnknownKeys = true
- classDiscriminator = "_typename"
- serializersModule = this@RootDecoder.serializersModule
- }
-
-}
\ No newline at end of file
diff --git a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/rootSchemeToSolid.kt b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/rootSchemeToSolid.kt
new file mode 100644
index 00000000..89849613
--- /dev/null
+++ b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/rootSchemeToSolid.kt
@@ -0,0 +1,217 @@
+package ru.mipt.npm.root
+
+import space.kscience.dataforge.meta.*
+import space.kscience.dataforge.names.Name
+import space.kscience.dataforge.names.asName
+import space.kscience.dataforge.names.plus
+import space.kscience.visionforge.solid.*
+import kotlin.math.PI
+import kotlin.math.atan2
+import kotlin.math.pow
+import kotlin.math.sqrt
+
+private val solidsName = "solids".asName()
+private val volumesName = "volumes".asName()
+
+private operator fun Number.times(d: Double) = toDouble() * d
+
+private operator fun Number.times(f: Float) = toFloat() * f
+
+private fun degToRad(d: Double) = d * PI / 180.0
+
+// converting to XYZ to Tait–Bryan angles according to https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
+private fun Solid.rotate(rot: DoubleArray) {
+ val xAngle = atan2(-rot[5], rot[8])
+ val yAngle = atan2(rot[2], sqrt(1.0 - rot[2].pow(2)))
+ val zAngle = atan2(-rot[1], rot[0])
+ rotation = Point3D(xAngle, yAngle, zAngle)
+}
+
+private fun Solid.translate(trans: DoubleArray) {
+ val (x, y, z) = trans
+ position = Point3D(x, y, z)
+}
+
+private fun Solid.useMatrix(matrix: TGeoMatrixScheme?) {
+ if (matrix == null) return
+ when (matrix.typename) {
+ "TGeoIdentity" -> {
+ //do nothing
+ }
+ "TGeoTranslation" -> {
+ val fTranslation by matrix.doubleArray()
+ translate(fTranslation)
+ }
+ "TGeoRotation" -> {
+ val fRotationMatrix by matrix.doubleArray()
+ rotate(fRotationMatrix)
+ }
+ "TGeoCombiTrans" -> {
+ val fTranslation by matrix.doubleArray()
+
+ translate(fTranslation)
+ if (matrix.meta["fRotationMatrix"] != null) {
+ val fRotationMatrix by matrix.doubleArray()
+ rotate(fRotationMatrix)
+ }
+ }
+ "TGeoHMatrix" -> {
+ val fTranslation by matrix.doubleArray()
+ val fRotationMatrix by matrix.doubleArray()
+ val fScale by matrix.doubleArray()
+ translate(fTranslation)
+ rotate(fRotationMatrix)
+ scale = Point3D(fScale[0], fScale[1], fScale[2])
+ }
+ }
+}
+
+private fun SolidGroup.addShape(shape: TGeoShapeScheme, refCache: RefCache) {
+ when (shape.typename) {
+ "TGeoCompositeShape" -> {
+ val bool by shape.spec(TGeoBoolNodeScheme)
+ val compositeType = when (bool.typename) {
+ "TGeoIntersection" -> CompositeType.INTERSECT
+ "TGeoSubtraction" -> CompositeType.SUBTRACT
+ "TGeoUnion" -> CompositeType.UNION
+ else -> error("Unknown bool node type ${bool.typename}")
+ }
+ composite(compositeType, name = shape.fName) {
+ addShape(bool.fLeft.resolve(refCache)!!, refCache).apply {
+ useMatrix(bool.fLeftMat.resolve(refCache))
+ }
+ addShape(bool.fRight.resolve(refCache)!!, refCache).apply {
+ useMatrix(bool.fRightMat.resolve(refCache))
+ }
+ }
+ }
+ "TGeoXtru" -> {
+ val fNvert by shape.meta.int(0)
+ val fX by shape.meta.doubleArray()
+ val fY by shape.meta.doubleArray()
+ val fNz by shape.meta.int(0)
+ val fZ by shape.meta.doubleArray()
+ val fX0 by shape.meta.doubleArray()
+ val fY0 by shape.meta.doubleArray()
+ val fScale by shape.meta.doubleArray()
+
+ extruded(name = shape.fName) {
+ (0 until fNvert).forEach { index ->
+ shape {
+ point(fX[index], fY[index])
+ }
+ }
+
+ (0 until fNz).forEach { index ->
+ layer(
+ fZ[index],
+ fX0[index],
+ fY0[index],
+ fScale[index]
+ )
+ }
+ }
+ }
+ "TGeoTube" -> {
+ val fRmax by shape.meta.double(0.0)
+ val fDz by shape.meta.double(0.0)
+ val fRmin by shape.meta.double(0.0)
+
+ tube(
+ radius = fRmax,
+ height = fDz * 2,
+ innerRadius = fRmin,
+ name = shape.fName
+ )
+ }
+ "TGeoTubeSeg" -> {
+ val fRmax by shape.meta.double(0.0)
+ val fDz by shape.meta.double(0.0)
+ val fRmin by shape.meta.double(0.0)
+ val fPhi1 by shape.meta.double(0.0)
+ val fPhi2 by shape.meta.double(0.0)
+
+ tube(
+ radius = fRmax,
+ height = fDz * 2,
+ innerRadius = fRmin,
+ startAngle = degToRad(fPhi1),
+ angle = degToRad(fPhi2 - fPhi1),
+ name = shape.fName
+ )
+ }
+ "TGeoPcon" -> {
+ TODO()
+ }
+ "TGeoPgon" -> {
+ TODO()
+ }
+ "TGeoShapeAssembly" -> {
+ val fVolume by shape.refSpec(TGeoVolumeScheme)
+ volume(fVolume.resolve(refCache)!!, refCache)
+ }
+ "TGeoBBox" -> {
+ box(shape.fDX * 2, shape.fDY * 2, shape.fDZ * 2, name = shape.fName)
+ }
+ }
+}
+
+private fun SolidGroup.node(obj: TGeoNodeScheme, refCache: RefCache) {
+ val volume = obj.fVolume.resolve(refCache)
+ if (volume != null) {
+ volume(volume, refCache, obj.fName).apply {
+ when (obj.typename) {
+ "TGeoNodeMatrix" -> {
+ val fMatrix by obj.refSpec(TGeoMatrixScheme)
+ useMatrix(fMatrix.resolve(refCache))
+ }
+ "TGeoNodeOffset" -> {
+ val fOffset by obj.meta.double(0.0)
+ x = fOffset
+ }
+ }
+ }
+ }
+}
+
+private fun buildGroup(volume: TGeoVolumeScheme, refCache: RefCache): SolidGroup = SolidGroup {
+ volume.fShape.resolve(refCache)?.let { addShape(it, refCache) }
+ volume.fNodes.let {
+ it.forEach { obj ->
+ node(obj.resolve(refCache)!!, refCache)
+ }
+ }
+}
+
+private val SolidGroup.rootPrototypes: SolidGroup get() = (parent as? SolidGroup)?.rootPrototypes ?: this
+
+private fun SolidGroup.volume(
+ volume: TGeoVolumeScheme,
+ refCache: RefCache,
+ name: String? = null,
+ cache: Boolean = true
+): Solid {
+ val group = buildGroup(volume, refCache)
+ val combinedName = if (volume.fName.isEmpty()) {
+ name
+ } else if (name == null) {
+ volume.fName
+ } else {
+ "${name}_${volume.fName}"
+ }
+ return if (!cache) {
+ set(combinedName?.let { Name.parse(it)},group)
+ group
+ } else newRef(
+ name = combinedName,
+ obj = group,
+ prototypeHolder = rootPrototypes,
+ templateName = volumesName + Name.parse(combinedName ?: "volume[${group.hashCode()}]")
+ )
+}
+
+public fun TGeoManagerScheme.toSolid(): SolidGroup = SolidGroup {
+ fNodes.forEach {
+ node(it.resolve(refCache)!!, refCache)
+ }
+}
\ No newline at end of file
diff --git a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/serialization/TGeoManager.kt b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/serialization/TGeoManager.kt
new file mode 100644
index 00000000..59957364
--- /dev/null
+++ b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/serialization/TGeoManager.kt
@@ -0,0 +1,17 @@
+package ru.mipt.npm.root.serialization
+
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+
+@Serializable
+@SerialName("TGeoManager")
+public class TGeoManager : TNamed() {
+
+ public val fMatrices: TObjArray = TObjArray.getEmpty()
+
+ public val fShapes: TObjArray = TObjArray.getEmpty()
+
+ public val fVolumes: TObjArray = TObjArray.getEmpty()
+
+ public val fNodes: TObjArray = TObjArray.getEmpty()
+}
diff --git a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/TGeoMaterial.kt b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/serialization/TGeoMaterial.kt
similarity index 72%
rename from cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/TGeoMaterial.kt
rename to cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/serialization/TGeoMaterial.kt
index f4883e0e..e13d30b4 100644
--- a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/TGeoMaterial.kt
+++ b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/serialization/TGeoMaterial.kt
@@ -1,4 +1,4 @@
-package ru.mipt.npm.root
+package ru.mipt.npm.root.serialization
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
diff --git a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/TGeoMatrix.kt b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/serialization/TGeoMatrix.kt
similarity index 94%
rename from cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/TGeoMatrix.kt
rename to cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/serialization/TGeoMatrix.kt
index 0c6ec7ab..63ee38a7 100644
--- a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/TGeoMatrix.kt
+++ b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/serialization/TGeoMatrix.kt
@@ -1,4 +1,4 @@
-package ru.mipt.npm.root
+package ru.mipt.npm.root.serialization
import kotlinx.serialization.Contextual
import kotlinx.serialization.SerialName
diff --git a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/TGeoMedium.kt b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/serialization/TGeoMedium.kt
similarity index 85%
rename from cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/TGeoMedium.kt
rename to cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/serialization/TGeoMedium.kt
index 72b72c4c..630826e8 100644
--- a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/TGeoMedium.kt
+++ b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/serialization/TGeoMedium.kt
@@ -1,4 +1,4 @@
-package ru.mipt.npm.root
+package ru.mipt.npm.root.serialization
import kotlinx.serialization.Contextual
import kotlinx.serialization.SerialName
diff --git a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/TGeoNode.kt b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/serialization/TGeoNode.kt
similarity index 89%
rename from cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/TGeoNode.kt
rename to cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/serialization/TGeoNode.kt
index 75a8ac05..1babbbfc 100644
--- a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/TGeoNode.kt
+++ b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/serialization/TGeoNode.kt
@@ -1,4 +1,4 @@
-package ru.mipt.npm.root
+package ru.mipt.npm.root.serialization
import kotlinx.serialization.Contextual
import kotlinx.serialization.SerialName
@@ -6,7 +6,7 @@ import kotlinx.serialization.Serializable
@Serializable
@SerialName("TGeoNode")
-public sealed class TGeoNode : TNamed() {
+public open class TGeoNode : TNamed() {
public val fGeoAtt: UInt = 0u
@Contextual
diff --git a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/TGeoShape.kt b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/serialization/TGeoShape.kt
similarity index 98%
rename from cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/TGeoShape.kt
rename to cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/serialization/TGeoShape.kt
index 7cf6876a..faf47121 100644
--- a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/TGeoShape.kt
+++ b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/serialization/TGeoShape.kt
@@ -1,4 +1,4 @@
-package ru.mipt.npm.root
+package ru.mipt.npm.root.serialization
import kotlinx.serialization.Contextual
import kotlinx.serialization.SerialName
diff --git a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/TGeoVolume.kt b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/serialization/TGeoVolume.kt
similarity index 91%
rename from cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/TGeoVolume.kt
rename to cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/serialization/TGeoVolume.kt
index 9823eb1f..77c99e54 100644
--- a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/TGeoVolume.kt
+++ b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/serialization/TGeoVolume.kt
@@ -1,4 +1,4 @@
-package ru.mipt.npm.root
+package ru.mipt.npm.root.serialization
import kotlinx.serialization.Contextual
import kotlinx.serialization.SerialName
@@ -15,7 +15,7 @@ public open class TGeoVolume : TNamed() {
public val fFillStyle: Int? = null
@Contextual
- public val fNodes: TObjArray? = null
+ public val fNodes: TObjArray<@Contextual TGeoNode>? = null
@Contextual
public val fShape: TGeoShape? = null
diff --git a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/TObject.kt b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/serialization/TObject.kt
similarity index 94%
rename from cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/TObject.kt
rename to cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/serialization/TObject.kt
index 063cadfe..644c05a4 100644
--- a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/TObject.kt
+++ b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/serialization/TObject.kt
@@ -1,4 +1,4 @@
-package ru.mipt.npm.root
+package ru.mipt.npm.root.serialization
import kotlinx.serialization.Contextual
import kotlinx.serialization.SerialName
diff --git a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/serialization/jsonToRoot.kt b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/serialization/jsonToRoot.kt
new file mode 100644
index 00000000..94079ccd
--- /dev/null
+++ b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/serialization/jsonToRoot.kt
@@ -0,0 +1,235 @@
+package ru.mipt.npm.root.serialization
+
+import kotlinx.serialization.DeserializationStrategy
+import kotlinx.serialization.ExperimentalSerializationApi
+import kotlinx.serialization.KSerializer
+import kotlinx.serialization.descriptors.SerialDescriptor
+import kotlinx.serialization.encoding.Decoder
+import kotlinx.serialization.json.*
+import kotlinx.serialization.modules.SerializersModule
+import kotlinx.serialization.modules.contextual
+import kotlinx.serialization.modules.polymorphic
+import kotlinx.serialization.modules.subclass
+
+
+private fun jsonRootDeserializer(
+ tSerializer: KSerializer,
+ builder: (JsonElement) -> T
+): DeserializationStrategy = object :
+ DeserializationStrategy {
+ private val jsonElementSerializer = JsonElement.serializer()
+
+ override val descriptor: SerialDescriptor
+ get() = jsonElementSerializer.descriptor
+
+ override fun deserialize(decoder: Decoder): T {
+ val json = decoder.decodeSerializableValue(jsonElementSerializer)
+ return builder(json)
+ }
+}
+
+/**
+ * Load Json encoded TObject
+ */
+public fun TObject.decodeFromJson(serializer: KSerializer, jsonElement: JsonElement): T =
+ RootDecoder.decode(serializer, jsonElement)
+
+public fun TObject.decodeFromString(serializer: KSerializer, string: String): T {
+ val json = Json.parseToJsonElement(string)
+ return RootDecoder.decode(serializer, json)
+}
+
+private object RootDecoder {
+
+ private class RootUnrefSerializer(
+ private val tSerializer: KSerializer,
+ private val refCache: List,
+ ) : KSerializer by tSerializer {
+
+ override fun deserialize(decoder: Decoder): T {
+ val input = decoder as JsonDecoder
+ val element = input.decodeJsonElement()
+ val refId = (element as? JsonObject)?.get("\$ref")?.jsonPrimitive?.int
+ val ref = if (refId != null) {
+ println("Substituting ${tSerializer.descriptor.serialName} ref $refId")
+ //Forward ref for shapes
+ when (tSerializer.descriptor.serialName) {
+ "TGeoShape" -> return TGeoShapeRef {
+ refCache[refId].getOrPutValue {
+ input.json.decodeFromJsonElement(tSerializer, it) as TGeoShape
+ }
+ } as T
+
+ "TGeoVolumeAssembly" -> return TGeoVolumeAssemblyRef {
+ refCache[refId].getOrPutValue {
+ input.json.decodeFromJsonElement(tSerializer, it) as TGeoVolumeAssembly
+ }
+ } as T
+
+ "TGeoVolume" -> return TGeoVolumeRef {
+ refCache[refId].getOrPutValue {
+ input.json.decodeFromJsonElement(tSerializer, it) as TGeoVolume
+ }
+ } as T
+
+ //Do unref
+ else -> refCache[refId]
+ }
+ } else {
+ refCache.find { it.element == element } ?: error("Element '$element' not found in the cache")
+ }
+
+ return ref.getOrPutValue {
+// println("Decoding $it")
+ val actualTypeName = it.jsonObject["_typename"]?.jsonPrimitive?.content
+ input.json.decodeFromJsonElement(tSerializer, it)
+ }
+ }
+ }
+
+ private fun KSerializer.unref(refCache: List): KSerializer = RootUnrefSerializer(this, refCache)
+
+ @OptIn(ExperimentalSerializationApi::class)
+ fun unrefSerializersModule(
+ refCache: List
+ ): SerializersModule = SerializersModule {
+
+ contextual(TObjArray::class) {
+ TObjArray.serializer(it[0]).unref(refCache)
+ }
+
+ contextual(TGeoMedium.serializer().unref(refCache))
+
+ polymorphic(TGeoBoolNode::class) {
+ subclass(TGeoIntersection.serializer().unref(refCache))
+ subclass(TGeoUnion.serializer().unref(refCache))
+ subclass(TGeoSubtraction.serializer().unref(refCache))
+ }
+
+ polymorphic(TGeoShape::class) {
+ subclass(TGeoBBox.serializer())
+ subclass(TGeoXtru.serializer())
+ subclass(TGeoTube.serializer())
+ subclass(TGeoTubeSeg.serializer())
+ subclass(TGeoPcon.serializer())
+ subclass(TGeoPgon.serializer())
+
+ subclass(TGeoCompositeShape.serializer().unref(refCache))
+ subclass(TGeoShapeAssembly.serializer().unref(refCache))
+
+ default {
+ if (it == null) {
+ TGeoShape.serializer().unref(refCache)
+ } else {
+ error("Unrecognized shape $it")
+ }
+ }
+ }
+
+ polymorphic(TGeoMatrix::class) {
+ subclass(TGeoIdentity.serializer())
+ subclass(TGeoHMatrix.serializer().unref(refCache))
+ subclass(TGeoTranslation.serializer())
+ subclass(TGeoRotation.serializer())
+ subclass(TGeoCombiTrans.serializer().unref(refCache))
+
+
+ val unrefed = TGeoMatrix.serializer().unref(refCache)
+ default {
+ if (it == null) {
+ unrefed
+ } else {
+ error("Unrecognized matrix $it")
+ }
+ }
+ }
+
+ polymorphic(TGeoVolume::class, TGeoVolume.serializer().unref(refCache)) {
+ subclass(TGeoVolumeAssembly.serializer().unref(refCache))
+
+ val unrefed = TGeoVolume.serializer().unref(refCache)
+ default {
+ if (it == null) {
+ unrefed
+ } else {
+ error("Unrecognized volume $it")
+ }
+ }
+ }
+
+ polymorphic(TGeoNode::class, TGeoNode.serializer().unref(refCache)) {
+ subclass(TGeoNodeMatrix.serializer().unref(refCache))
+ subclass(TGeoNodeOffset.serializer().unref(refCache))
+
+ val unrefed = TGeoNode.serializer().unref(refCache)
+ default {
+ if (it == null) {
+ unrefed
+ } else {
+ error("Unrecognized node $it")
+ }
+ }
+ }
+ }
+
+ /**
+ * Create an instance of Json with unfolding Root references. This instance could not be reused because of the cache.
+ */
+ private fun unrefJson(refCache: MutableList): Json = Json {
+ encodeDefaults = true
+ ignoreUnknownKeys = true
+ classDiscriminator = "_typename"
+ serializersModule = unrefSerializersModule(refCache)
+ }
+
+
+ fun decode(sourceDeserializer: KSerializer, source: JsonElement): T {
+ val refCache = ArrayList()
+
+ fun fillCache(element: JsonElement) {
+ when (element) {
+ is JsonObject -> {
+ if (element["_typename"] != null) {
+ refCache.add(RefEntry(element))
+ }
+ element.values.forEach {
+ fillCache(it)
+ }
+ }
+ is JsonArray -> {
+ element.forEach {
+ fillCache(it)
+ }
+ }
+ else -> {
+ //ignore primitives
+ }
+ }
+ }
+ fillCache(source)
+
+ return unrefJson(refCache).decodeFromJsonElement(sourceDeserializer.unref(refCache), source)
+ }
+
+ class RefEntry(val element: JsonElement) {
+
+ var value: Any? = null
+
+ fun getOrPutValue(builder: (JsonElement) -> T): T {
+ if (value == null) {
+ value = builder(element)
+ }
+ return value as T
+ }
+
+ override fun toString(): String = element.toString()
+ }
+
+// val json = Json {
+// encodeDefaults = true
+// ignoreUnknownKeys = true
+// classDiscriminator = "_typename"
+// serializersModule = this@RootDecoder.serializersModule
+// }
+
+}
\ No newline at end of file
diff --git a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/rootToSolid.kt b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/serialization/rootToSolid.kt
similarity index 99%
rename from cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/rootToSolid.kt
rename to cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/serialization/rootToSolid.kt
index ee14324d..117e51b3 100644
--- a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/rootToSolid.kt
+++ b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/serialization/rootToSolid.kt
@@ -1,4 +1,4 @@
-package ru.mipt.npm.root
+package ru.mipt.npm.root.serialization
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.asName
diff --git a/cern-root-loader/src/jvmTest/kotlin/ru/mipt/npm/root/loadBMN.kt b/cern-root-loader/src/jvmTest/kotlin/ru/mipt/npm/root/loadBMN.kt
index 7fcd1e22..fbd7482e 100644
--- a/cern-root-loader/src/jvmTest/kotlin/ru/mipt/npm/root/loadBMN.kt
+++ b/cern-root-loader/src/jvmTest/kotlin/ru/mipt/npm/root/loadBMN.kt
@@ -1,12 +1,16 @@
package ru.mipt.npm.root
-import kotlinx.serialization.json.*
+import kotlinx.serialization.json.JsonArray
+import kotlinx.serialization.json.JsonElement
+import kotlinx.serialization.json.JsonObject
+import kotlinx.serialization.json.jsonPrimitive
+import ru.mipt.npm.root.serialization.TGeoManager
import space.kscience.visionforge.solid.Solids
import java.time.Duration
import kotlin.system.measureTimeMillis
private fun JsonElement.countTypes(): Sequence = sequence {
- when (val json = this@countTypes){
+ when (val json = this@countTypes) {
is JsonObject -> {
json["_typename"]?.let { yield(it.jsonPrimitive.content) }
json.values.forEach { yieldAll(it.countTypes()) }
@@ -16,25 +20,33 @@ private fun JsonElement.countTypes(): Sequence = sequence {
yieldAll(it.countTypes())
}
}
- else -> {}
+ else -> {
+ }
}
}
fun main() {
val string = TGeoManager::class.java.getResourceAsStream("/BM@N.root.json")!!
.readAllBytes().decodeToString()
- val json = Json.parseToJsonElement(string)
- val sizes = json.countTypes().groupBy { it }.mapValues { it.value.size }
- sizes.forEach {
- println(it)
- }
-
val time = measureTimeMillis {
- val geo = TObject.decodeFromString(TGeoManager.serializer(), string)
+ val geo = TGeoManagerScheme.parse(string)
val solid = geo.toSolid()
println(Solids.encodeToString(solid))
}
+// val json = Json.parseToJsonElement(string)
+// val sizes = json.countTypes().groupBy { it }.mapValues { it.value.size }
+// sizes.forEach {
+// println(it)
+// }
+//
+// val time = measureTimeMillis {
+// val geo = TObject.decodeFromString(TGeoManager.serializer(), string)
+// val solid = geo.toSolid()
+//
+// println(Solids.encodeToString(solid))
+// }
+//
println(Duration.ofMillis(time))
}
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
index 5bce5b02..0ed8858a 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,7 +1,5 @@
kotlin.code.style=official
-kotlin.mpp.enableGranularSourceSetsMetadata=true
kotlin.mpp.stability.nowarn=true
-kotlin.native.enableDependencyPropagation=false
#kotlin.jupyter.add.scanner=false