forked from kscience/visionforge
dynamic root scheme parser
This commit is contained in:
parent
68704086e9
commit
7b5faaa61e
@ -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()
|
||||
}
|
@ -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<Meta>
|
||||
|
||||
public interface ObjectRef<T : TObjectScheme> {
|
||||
public fun resolve(refCache: RefCache): T?
|
||||
}
|
||||
|
||||
private class ChildObjectRef<T : TObjectScheme>(
|
||||
val spec: Specification<T>,
|
||||
val metaProvider: () -> Meta?
|
||||
) : ObjectRef<T> {
|
||||
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 <T: TObjectScheme> List<ObjectRef<T>>.resolve(refCache: RefCache): List<T> = 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 <T : TObjectScheme> tObjectArray(
|
||||
spec: Specification<T>
|
||||
): ReadOnlyProperty<Any?, List<ObjectRef<T>>> = ReadOnlyProperty { _, property ->
|
||||
meta.getIndexed(Name.of(property.name, "arr")).values.map { ChildObjectRef(spec){it} }
|
||||
}
|
||||
|
||||
internal fun <T : TObjectScheme> refSpec(
|
||||
spec: Specification<T>,
|
||||
key: Name? = null
|
||||
): ReadOnlyProperty<Any?, ObjectRef<T>> = ReadOnlyProperty { _, property ->
|
||||
ChildObjectRef(spec) { meta[key ?: property.name.asName()] }
|
||||
}
|
||||
|
||||
public companion object : SchemeSpec<TObjectScheme>(::TObjectScheme)
|
||||
}
|
||||
|
||||
public open class TNamedScheme : TObjectScheme() {
|
||||
public val fName: String by string("")
|
||||
public val fTitle: String by string("")
|
||||
|
||||
public companion object : SchemeSpec<TNamedScheme>(::TNamedScheme)
|
||||
}
|
||||
|
||||
public class TGeoMaterialScheme : TNamedScheme() {
|
||||
|
||||
public companion object : SchemeSpec<TGeoMaterialScheme>(::TGeoMaterialScheme)
|
||||
}
|
||||
|
||||
public class TGeoMediumScheme : TNamedScheme() {
|
||||
public val fMaterial: ObjectRef<TGeoMaterialScheme> by refSpec(TGeoMaterialScheme)
|
||||
public val fParams: DoubleArray by doubleArray()
|
||||
|
||||
public companion object : SchemeSpec<TGeoMediumScheme>(::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>(::TGeoShapeScheme)
|
||||
}
|
||||
|
||||
public class TGeoVolumeScheme : TNamedScheme() {
|
||||
public val fNodes: List<ObjectRef<TGeoNodeScheme>> by tObjectArray(TGeoNodeScheme)
|
||||
public val fShape: ObjectRef<TGeoShapeScheme> by refSpec(TGeoShapeScheme)
|
||||
public val fMedium: ObjectRef<TGeoMediumScheme> by refSpec(TGeoMediumScheme)
|
||||
|
||||
public companion object : SchemeSpec<TGeoVolumeScheme>(::TGeoVolumeScheme)
|
||||
}
|
||||
|
||||
public class TGeoNodeScheme : TNamedScheme() {
|
||||
public val fVolume: ObjectRef<TGeoVolumeScheme> by refSpec(TGeoVolumeScheme)
|
||||
|
||||
public companion object : SchemeSpec<TGeoNodeScheme>(::TGeoNodeScheme)
|
||||
}
|
||||
|
||||
public class TGeoMatrixScheme : TNamedScheme() {
|
||||
public companion object : SchemeSpec<TGeoMatrixScheme>(::TGeoMatrixScheme)
|
||||
}
|
||||
|
||||
|
||||
public class TGeoBoolNodeScheme : TObjectScheme() {
|
||||
public val fLeft: ObjectRef<TGeoShapeScheme> by refSpec(TGeoShapeScheme)
|
||||
public val fLeftMat: ObjectRef<TGeoMatrixScheme> by refSpec(TGeoMatrixScheme)
|
||||
|
||||
public val fRight: ObjectRef<TGeoShapeScheme> by refSpec(TGeoShapeScheme)
|
||||
public val fRightMat: ObjectRef<TGeoMatrixScheme> by refSpec(TGeoMatrixScheme)
|
||||
|
||||
public companion object : SchemeSpec<TGeoBoolNodeScheme>(::TGeoBoolNodeScheme)
|
||||
}
|
||||
|
||||
|
||||
public class TGeoManagerScheme : TNamedScheme() {
|
||||
public val fMatrices: List<ObjectRef<TGeoMatrixScheme>> by tObjectArray(TGeoMatrixScheme)
|
||||
|
||||
public val fShapes: List<ObjectRef<TGeoShapeScheme>> by tObjectArray(TGeoShapeScheme)
|
||||
|
||||
public val fVolumes: List<ObjectRef<TGeoVolumeScheme>> by tObjectArray(TGeoVolumeScheme)
|
||||
|
||||
public val fNodes: List<ObjectRef<TGeoNodeScheme>> by tObjectArray(TGeoNodeScheme)
|
||||
|
||||
public val refCache: List<Meta> by lazy {
|
||||
val res = ArrayList<Meta>(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>(::TGeoManagerScheme) {
|
||||
|
||||
public fun parse(string: String): TGeoManagerScheme {
|
||||
val meta = Json.decodeFromString(MetaSerializer, string)
|
||||
return read(meta)
|
||||
}
|
||||
}
|
||||
}
|
@ -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 <T> jsonRootDeserializer(tSerializer: KSerializer<T>, builder: (JsonElement) -> T): DeserializationStrategy<T> = object :
|
||||
DeserializationStrategy<T> {
|
||||
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 <T: TObject> TObject.Companion.decodeFromJson(serializer: KSerializer<T>, jsonElement: JsonElement): T =
|
||||
RootDecoder.decode(serializer, jsonElement)
|
||||
|
||||
public fun <T: TObject> TObject.Companion.decodeFromString(serializer: KSerializer<T>, string: String): T {
|
||||
val json = RootDecoder.json.parseToJsonElement(string)
|
||||
return RootDecoder.decode(serializer, json)
|
||||
}
|
||||
|
||||
private object RootDecoder {
|
||||
|
||||
private class RootUnrefSerializer<T>(
|
||||
private val tSerializer: KSerializer<T>,
|
||||
private val refCache: List<RefEntry>,
|
||||
) : KSerializer<T> 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 <T> KSerializer<T>.unref(refCache: List<RefEntry>): KSerializer<T> =
|
||||
RootUnrefSerializer(this, refCache)
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
fun unrefSerializersModule(
|
||||
refCache: List<RefEntry>
|
||||
): 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<RefEntry>): Json = Json {
|
||||
encodeDefaults = true
|
||||
ignoreUnknownKeys = true
|
||||
classDiscriminator = "_typename"
|
||||
serializersModule = unrefSerializersModule(refCache)
|
||||
}
|
||||
|
||||
|
||||
fun <T: TObject> decode(sourceDeserializer: KSerializer<T>, source: JsonElement): T {
|
||||
val refCache = ArrayList<RefEntry>()
|
||||
|
||||
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 <T> getOrPutValue(builder: (JsonElement) -> T): T {
|
||||
if (value == null) {
|
||||
value = builder(element)
|
||||
}
|
||||
return value as T
|
||||
}
|
||||
|
||||
override fun toString(): String = element.toString()
|
||||
}
|
||||
|
||||
private fun PolymorphicModuleBuilder<TGeoShape>.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<TGeoMatrix>.matrices() {
|
||||
subclass(TGeoIdentity.serializer())
|
||||
subclass(TGeoHMatrix.serializer())
|
||||
subclass(TGeoTranslation.serializer())
|
||||
subclass(TGeoRotation.serializer())
|
||||
subclass(TGeoCombiTrans.serializer())
|
||||
}
|
||||
|
||||
private fun PolymorphicModuleBuilder<TGeoBoolNode>.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
|
||||
}
|
||||
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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<TGeoMatrix> = TObjArray.getEmpty()
|
||||
|
||||
public val fShapes: TObjArray<TGeoShape> = TObjArray.getEmpty()
|
||||
|
||||
public val fVolumes: TObjArray<TGeoVolume> = TObjArray.getEmpty()
|
||||
|
||||
public val fNodes: TObjArray<TGeoNode> = TObjArray.getEmpty()
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package ru.mipt.npm.root
|
||||
package ru.mipt.npm.root.serialization
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
@ -1,4 +1,4 @@
|
||||
package ru.mipt.npm.root
|
||||
package ru.mipt.npm.root.serialization
|
||||
|
||||
import kotlinx.serialization.Contextual
|
||||
import kotlinx.serialization.SerialName
|
@ -1,4 +1,4 @@
|
||||
package ru.mipt.npm.root
|
||||
package ru.mipt.npm.root.serialization
|
||||
|
||||
import kotlinx.serialization.Contextual
|
||||
import kotlinx.serialization.SerialName
|
@ -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
|
@ -1,4 +1,4 @@
|
||||
package ru.mipt.npm.root
|
||||
package ru.mipt.npm.root.serialization
|
||||
|
||||
import kotlinx.serialization.Contextual
|
||||
import kotlinx.serialization.SerialName
|
@ -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<TGeoNode>? = null
|
||||
public val fNodes: TObjArray<@Contextual TGeoNode>? = null
|
||||
|
||||
@Contextual
|
||||
public val fShape: TGeoShape? = null
|
@ -1,4 +1,4 @@
|
||||
package ru.mipt.npm.root
|
||||
package ru.mipt.npm.root.serialization
|
||||
|
||||
import kotlinx.serialization.Contextual
|
||||
import kotlinx.serialization.SerialName
|
@ -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 <T> jsonRootDeserializer(
|
||||
tSerializer: KSerializer<T>,
|
||||
builder: (JsonElement) -> T
|
||||
): DeserializationStrategy<T> = object :
|
||||
DeserializationStrategy<T> {
|
||||
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 <T : TObject> TObject.decodeFromJson(serializer: KSerializer<T>, jsonElement: JsonElement): T =
|
||||
RootDecoder.decode(serializer, jsonElement)
|
||||
|
||||
public fun <T : TObject> TObject.decodeFromString(serializer: KSerializer<T>, string: String): T {
|
||||
val json = Json.parseToJsonElement(string)
|
||||
return RootDecoder.decode(serializer, json)
|
||||
}
|
||||
|
||||
private object RootDecoder {
|
||||
|
||||
private class RootUnrefSerializer<T>(
|
||||
private val tSerializer: KSerializer<T>,
|
||||
private val refCache: List<RefEntry>,
|
||||
) : KSerializer<T> 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 <T> KSerializer<T>.unref(refCache: List<RefEntry>): KSerializer<T> = RootUnrefSerializer(this, refCache)
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
fun unrefSerializersModule(
|
||||
refCache: List<RefEntry>
|
||||
): 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<RefEntry>): Json = Json {
|
||||
encodeDefaults = true
|
||||
ignoreUnknownKeys = true
|
||||
classDiscriminator = "_typename"
|
||||
serializersModule = unrefSerializersModule(refCache)
|
||||
}
|
||||
|
||||
|
||||
fun <T : TObject> decode(sourceDeserializer: KSerializer<T>, source: JsonElement): T {
|
||||
val refCache = ArrayList<RefEntry>()
|
||||
|
||||
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 <T> 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
|
||||
// }
|
||||
|
||||
}
|
@ -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
|
@ -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<String> = 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<String> = 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))
|
||||
}
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user