diff --git a/build.gradle.kts b/build.gradle.kts index cd6ece5d..20d1a1bd 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,7 +1,7 @@ plugins { id("ru.mipt.npm.gradle.project") -// kotlin("multiplatform") version "1.5.30-RC" apply false -// kotlin("js") version "1.5.30-RC" apply false + kotlin("multiplatform") version "1.5.30" apply false + kotlin("js") version "1.5.30" apply false } val dataforgeVersion by extra("0.5.1") diff --git a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/JsonRootObject.kt b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/JsonRootObject.kt deleted file mode 100644 index bb5fd1cb..00000000 --- a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/JsonRootObject.kt +++ /dev/null @@ -1,26 +0,0 @@ -package ru.mipt.npm.root - -import kotlinx.serialization.KSerializer -import kotlinx.serialization.Serializable -import kotlinx.serialization.descriptors.SerialDescriptor -import kotlinx.serialization.encoding.Decoder -import kotlinx.serialization.encoding.Encoder -import kotlinx.serialization.json.JsonElement - -@Serializable(JsonRootSerializer::class) -public class JsonRootObject(public val element: JsonElement): TObject() - -public object JsonRootSerializer: KSerializer{ - private val jsonElementSerializer = JsonElement.serializer() - - override val descriptor: SerialDescriptor - get() = jsonElementSerializer.descriptor - - override fun deserialize(decoder: Decoder): JsonRootObject { - return JsonRootObject(decoder.decodeSerializableValue(jsonElementSerializer)) - } - - override fun serialize(encoder: Encoder, value: JsonRootObject) { - encoder.encodeSerializableValue(jsonElementSerializer, value.element) - } -} \ No newline at end of file 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 index 96b02768..41607fbd 100644 --- 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 @@ -9,11 +9,14 @@ import kotlinx.serialization.Serializable public class TGeoManager : TNamed() { @Contextual - public val fMatrices: TObjArray = TObjArray.empty + public val fMatrices: TObjArray = TObjArray.getEmpty() @Contextual - public val fShapes: TObjArray = TObjArray.empty + public val fShapes: TObjArray = TObjArray.getEmpty() @Contextual - public val fVolumes: TObjArray = TObjArray.empty + public val fVolumes: TObjArray = TObjArray.getEmpty() + + @Contextual + public val fNodes: TObjArray = TObjArray.getEmpty() } 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/TGeoNode.kt index 9e6f47f1..75a8ac05 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/TGeoNode.kt @@ -6,7 +6,7 @@ import kotlinx.serialization.Serializable @Serializable @SerialName("TGeoNode") -public open class TGeoNode : TNamed() { +public sealed class TGeoNode : TNamed() { public val fGeoAtt: UInt = 0u @Contextual @@ -25,4 +25,10 @@ public open class TGeoNode : TNamed() { public class TGeoNodeMatrix : TGeoNode() { @Contextual public val fMatrix: TGeoMatrix? = null +} + +@Serializable +@SerialName("TGeoNodeOffset") +public class TGeoNodeOffset : TGeoNode() { + public val fOffset: Double = 0.0 } \ No newline at end of file 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/TGeoShape.kt index 26cf0bab..7cf6876a 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/TGeoShape.kt @@ -3,6 +3,7 @@ package ru.mipt.npm.root import kotlinx.serialization.Contextual import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +import kotlin.math.PI @Serializable @SerialName("TGeoShape") @@ -27,13 +28,13 @@ public sealed class TGeoBoolNode : TObject() { public abstract val fLeft: TGeoShape @Contextual - public abstract val fLeftMat: TGeoMatrix + public val fLeftMat: TGeoMatrix? = null @Contextual public abstract val fRight: TGeoShape @Contextual - public abstract val fRightMat: TGeoMatrix + public val fRightMat: TGeoMatrix? = null } @Serializable @@ -42,11 +43,7 @@ public class TGeoUnion( @Contextual override val fLeft: TGeoShape, @Contextual - override val fLeftMat: TGeoMatrix, - @Contextual override val fRight: TGeoShape, - @Contextual - override val fRightMat: TGeoMatrix ) : TGeoBoolNode() @Serializable @@ -55,11 +52,7 @@ public class TGeoSubtraction( @Contextual override val fLeft: TGeoShape, @Contextual - override val fLeftMat: TGeoMatrix, - @Contextual override val fRight: TGeoShape, - @Contextual - override val fRightMat: TGeoMatrix ) : TGeoBoolNode() @Serializable @@ -68,11 +61,7 @@ public class TGeoIntersection( @Contextual override val fLeft: TGeoShape, @Contextual - override val fLeftMat: TGeoMatrix, - @Contextual override val fRight: TGeoShape, - @Contextual - override val fRightMat: TGeoMatrix ) : TGeoBoolNode() @@ -97,18 +86,15 @@ public class TGeoXtru( @Serializable @SerialName("TGeoTube") -public open class TGeoTube( - public val fRmin: Double, - public val fRmax: Double, - public val fDz: Double, -) : TGeoBBox() +public open class TGeoTube : TGeoBBox() { + public val fRmin: Double = 0.0 + public val fRmax: Double = 0.0 + public val fDz: Double = 0.0 +} @Serializable @SerialName("TGeoTubeSeg") public class TGeoTubeSeg( - public val fRmin: Double, - public val fRmax: Double, - public val fDz: Double, public val fPhi1: Double, public val fPhi2: Double, public val fS1: Double, @@ -118,7 +104,24 @@ public class TGeoTubeSeg( public val fSm: Double, public val fCm: Double, public val fCdfi: Double, -) : TGeoBBox() +) : TGeoTube() + +@Serializable +@SerialName("TGeoPcon") +public open class TGeoPcon : TGeoBBox() { + public val fNz: Int = 0 // number of z planes (at least two) + public val fPhi1: Double = 0.0 // lower phi limit (converted to [0,2*pi) + public val fDphi: Double = PI * 2 // phi range + public val fRmin: DoubleArray = doubleArrayOf() //[fNz] pointer to array of inner radii + public val fRmax: DoubleArray = doubleArrayOf() //[fNz] pointer to array of outer radii + public val fZ: DoubleArray = doubleArrayOf() //[fNz] pointer to array of Z planes positions +} + +@Serializable +@SerialName("TGeoPgon") +public open class TGeoPgon : TGeoPcon() { + public val fNedges: Int = 0 +} @Serializable @SerialName("TGeoShapeAssembly") 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/TGeoVolume.kt index 480c1ca7..9823eb1f 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/TGeoVolume.kt @@ -15,7 +15,7 @@ public open class TGeoVolume : TNamed() { public val fFillStyle: Int? = null @Contextual - public val fNodes: TObjArray? = null + public val fNodes: TObjArray? = 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/TObject.kt index 50738d11..063cadfe 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/TObject.kt @@ -19,9 +19,9 @@ public open class TNamed : TObject() { @Serializable @SerialName("TObjArray") -public class TObjArray(public val arr: List<@Contextual TObject>): TObject() { +public class TObjArray(public val arr: List<@Contextual T>): TObject() { public companion object{ - public val empty: TObjArray = TObjArray(emptyList()) + public fun getEmpty(): TObjArray = TObjArray(emptyList()) } } diff --git a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/rootJson.kt b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/jsonToRoot.kt similarity index 71% rename from cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/rootJson.kt rename to cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/jsonToRoot.kt index 8416368a..d0c3afb9 100644 --- a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/rootJson.kt +++ b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/jsonToRoot.kt @@ -1,12 +1,27 @@ 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 */ @@ -22,8 +37,7 @@ private object RootDecoder { private class RootUnrefSerializer( private val tSerializer: KSerializer, - private val refCache: List,// = ArrayList(4096) - //private val counter: ReferenceCounter + private val refCache: List, ) : KSerializer by tSerializer { override fun deserialize(decoder: Decoder): T { @@ -31,14 +45,14 @@ private object RootDecoder { val element = input.decodeJsonElement() val refId = (element as? JsonObject)?.get("\$ref")?.jsonPrimitive?.int val ref = if (refId != null) { - //println("Substituting ref $refId") + //println("Substituting ${tSerializer.descriptor.serialName} ref $refId") //Forward ref for shapes when (tSerializer.descriptor.serialName) { - "TGeoShape" -> return TGeoShapeRef{ + "TGeoShape" -> return TGeoShapeRef { //Should be not null at the moment of actualization refCache[refId].value as TGeoShape } as T - "TGeoVolumeAssembly" -> return TGeoVolumeAssemblyRef{ + "TGeoVolumeAssembly" -> return TGeoVolumeAssemblyRef { //Should be not null at the moment of actualization refCache[refId].value as TGeoVolumeAssembly } as T @@ -66,24 +80,50 @@ private object RootDecoder { include(serializersModule) contextual(TGeoManager.serializer().unref(refCache)) - contextual(TObjArray.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) { - TGeoShape.serializer().unref(refCache) + if (it == null) { + TGeoShape.serializer().unref(refCache) + } else { + error("Unrecognized shape $it") + } } polymorphicDefault(TGeoMatrix::class) { - TGeoMatrix.serializer().unref(refCache) + 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") + } } } @@ -126,14 +166,6 @@ private object RootDecoder { return unrefJson(refCache).decodeFromJsonElement(sourceDeserializer.unref(refCache), source) } -// class ReferenceCounter(var value: Int = 0) { -// fun increment() { -// value += 1 -// } -// -// override fun toString(): String = value.toString() -// } - class RefEntry(val element: JsonElement) { var value: Any? = null @@ -154,6 +186,8 @@ private object RootDecoder { subclass(TGeoXtru.serializer()) subclass(TGeoTube.serializer()) subclass(TGeoTubeSeg.serializer()) + subclass(TGeoPcon.serializer()) + subclass(TGeoPgon.serializer()) subclass(TGeoShapeAssembly.serializer()) } @@ -173,25 +207,24 @@ private object RootDecoder { private val serializersModule = SerializersModule { - polymorphic(TObject::class) { - default { JsonRootSerializer } - subclass(TObjArray.serializer()) - - 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(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() @@ -205,8 +238,9 @@ private object RootDecoder { boolNodes() } - polymorphic(TGeoNode::class, TGeoNode.serializer()) { + polymorphic(TGeoNode::class) { subclass(TGeoNodeMatrix.serializer()) + subclass(TGeoNodeOffset.serializer()) } polymorphic(TGeoVolume::class, TGeoVolume.serializer()) { 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/rootToSolid.kt new file mode 100644 index 00000000..ebfb7b06 --- /dev/null +++ b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/rootToSolid.kt @@ -0,0 +1,176 @@ +package ru.mipt.npm.root + +import space.kscience.dataforge.names.Name +import space.kscience.dataforge.names.asName +import space.kscience.visionforge.setPropertyNode +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 + +private class GdmlLoader { + + // 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: TGeoMatrix?) { + when (matrix) { + null, is TGeoIdentity -> { + //do nothing + } + is TGeoTranslation -> { + translate(matrix.fTranslation) + } + is TGeoRotation -> { + rotate(matrix.fRotationMatrix) + } + is TGeoCombiTrans -> { + translate(matrix.fTranslation) + matrix.fRotation?.let { rotate(it.fRotationMatrix) } + } + is TGeoHMatrix -> { + translate(matrix.fTranslation) + rotate(matrix.fRotationMatrix) + val (xScale, yScale, zScale) = matrix.fScale + scale = Point3D(xScale, yScale, zScale) + } + } + } + + fun SolidGroup.addShape(shape: TGeoShape) { + when (shape) { + is TGeoShapeRef -> addShape(shape.value) + is TGeoCompositeShape -> { + val bool: TGeoBoolNode = shape.fNode + val compositeType = when (bool) { + is TGeoIntersection -> CompositeType.INTERSECT + is TGeoSubtraction -> CompositeType.SUBTRACT + is TGeoUnion -> CompositeType.UNION + } + composite(compositeType, name = shape.fName) { + addShape(bool.fLeft).apply { + useMatrix(bool.fLeftMat) + } + addShape(bool.fRight).apply { + useMatrix(bool.fRightMat) + } + } + } + is TGeoXtru -> extruded(name = shape.fName) { + + (0 until shape.fNvert).forEach { index -> + shape { + point(shape.fX[index], shape.fY[index]) + } + } + + (0 until shape.fNz).forEach { index -> + layer( + shape.fZ[index], + shape.fX0[index], + shape.fY0[index], + shape.fScale[index] + ) + } + } + is TGeoTube -> tube( + radius = shape.fRmax, + height = shape.fDz * 2, + innerRadius = shape.fRmin, + name = shape.fName + ) + is TGeoTubeSeg -> tube( + radius = shape.fRmax, + height = shape.fDz * 2, + innerRadius = shape.fRmin, + startAngle = degToRad(shape.fPhi1), + angle = degToRad(shape.fPhi2 - shape.fPhi1), + name = shape.fName + ) + is TGeoPcon -> TODO() + is TGeoPgon -> TODO() + is TGeoShapeAssembly -> volume(shape.fVolume) + is TGeoBBox -> box(shape.fDX * 2, shape.fDY * 2, shape.fDZ * 2, name = shape.fName) + } + } + + fun SolidGroup.node(obj: TGeoNode) { + if (obj.fVolume != null) { + volume(obj.fVolume).apply { + when (obj) { + is TGeoNodeMatrix -> { + useMatrix(obj.fMatrix) + } + is TGeoNodeOffset -> { + x = obj.fOffset + } + } + } + } + } + + fun buildGroup(volume: TGeoVolume): SolidGroup { + return if (volume is TGeoVolumeAssemblyRef) { + buildGroup(volume.value) + } else { + SolidGroup { + volume.fShape?.let { addShape(it) } + volume.fNodes?.let { + it.arr.forEach { obj -> + node(obj) + } + } + } + } + } + + private val SolidGroup.rootPrototypes: SolidGroup get() = (parent as? SolidGroup)?.rootPrototypes ?: this + + fun SolidGroup.volume(volume: TGeoVolume): SolidGroup { + val group = buildGroup(volume) + val ref = rootPrototypes.prototypes { + + } + set(volume.fName.ifEmpty { null }?.asName(), group) + return group + } + + fun load(geo: TGeoManager): SolidGroup { + /** + * A special group for local templates + */ + val proto = SolidGroup() + + val solids = proto.group(solidsName) { + setPropertyNode("edges.enabled", false) + } + + val volumes = proto.group(volumesName) + + val referenceStore = HashMap>() + + TODO() + } + +} \ No newline at end of file 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 763b7e45..c6cec829 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 @@ -31,6 +31,7 @@ fun main() { val time = measureTimeMillis { val geo = TObject.decodeFromString(TGeoManager.serializer(), string) + println(geo) } println(Duration.ofMillis(time)) diff --git a/gradle.properties b/gradle.properties index 511d4130..5bce5b02 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ kotlin.mpp.enableGranularSourceSetsMetadata=true kotlin.mpp.stability.nowarn=true kotlin.native.enableDependencyPropagation=false -kotlin.jupyter.add.scanner=false +#kotlin.jupyter.add.scanner=false org.gradle.jvmargs=-XX:MaxMetaspaceSize=1G org.gradle.parallel=true \ No newline at end of file diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/geometry.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/geometry.kt index 31611dc9..ccff7dcd 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/geometry.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/geometry.kt @@ -5,7 +5,10 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder -import space.kscience.dataforge.meta.* +import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.MetaProvider +import space.kscience.dataforge.meta.float +import space.kscience.dataforge.meta.get import space.kscience.visionforge.solid.Solid.Companion.X_KEY import space.kscience.visionforge.solid.Solid.Companion.Y_KEY import space.kscience.visionforge.solid.Solid.Companion.Z_KEY @@ -54,7 +57,7 @@ internal object Point3DSerializer : KSerializer { override val descriptor: SerialDescriptor = Point3DImpl.serializer().descriptor - override fun deserialize(decoder: Decoder): Point3D = decoder.decodeSerializableValue(Point3DImpl.serializer()) + override fun deserialize(decoder: Decoder): MutablePoint3D = decoder.decodeSerializableValue(Point3DImpl.serializer()) override fun serialize(encoder: Encoder, value: Point3D) { val impl: Point3DImpl = (value as? Point3DImpl) ?: Point3DImpl(value.x, value.y, value.z)