feature/root #69
@ -0,0 +1,160 @@
|
||||
package ru.mipt.npm.root
|
||||
|
||||
import kotlinx.serialization.json.Json
|
||||
import space.kscience.dataforge.meta.*
|
||||
import space.kscience.dataforge.misc.Named
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.asName
|
||||
import space.kscience.dataforge.values.doubleArray
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
|
||||
public fun MetaProvider.doubleArray(
|
||||
vararg default: Double,
|
||||
key: Name? = null,
|
||||
): ReadOnlyProperty<Any?, DoubleArray> = value(key) {
|
||||
it?.doubleArray ?: doubleArrayOf(*default)
|
||||
}
|
||||
|
||||
|
||||
public class DObjectCache(private val cache: List<Meta>, public val refStack: List<Int> = emptyList()) {
|
||||
public operator fun get(index: Int): Meta = cache[index]
|
||||
|
||||
public fun stack(ref: Int): DObjectCache = DObjectCache(cache, refStack + ref)
|
||||
|
||||
public companion object {
|
||||
public val empty: DObjectCache = DObjectCache(emptyList(), emptyList())
|
||||
}
|
||||
}
|
||||
//
|
||||
//public interface ObjectRef<T : DObject> {
|
||||
// public fun resolve(): T?
|
||||
//}
|
||||
|
||||
//public class ChildObjectRef<T : DObject>(
|
||||
// public val builder: (Meta, DObjectCache) -> T,
|
||||
// public val refCache: DObjectCache,
|
||||
// public val metaProvider: () -> Meta?
|
||||
//) : ObjectRef<T> {
|
||||
// override fun resolve(): T? {
|
||||
// val meta = metaProvider() ?: return null
|
||||
// meta["\$ref"]?.int?.let { refId ->
|
||||
// if (refCache.refStack.contains(refId)) {
|
||||
// println("Circular reference $refId in stack ${refCache.refStack}")
|
||||
// return null
|
||||
// }
|
||||
// return builder(refCache[refId], refCache.stack(refId))
|
||||
// }
|
||||
// return builder(meta, refCache)
|
||||
// }
|
||||
//}
|
||||
|
||||
public open class DObject(public val meta: Meta, private val refCache: DObjectCache) {
|
||||
|
||||
public val typename: String by meta.string(key = "_typename".asName()) {
|
||||
error("Type is not defined")
|
||||
}
|
||||
|
||||
private fun <T : DObject> resolve(builder: (Meta, DObjectCache) -> T, meta: Meta): T? {
|
||||
meta["\$ref"]?.int?.let { refId ->
|
||||
if (refCache.refStack.contains(refId)) {
|
||||
println("Circular reference $refId in stack ${refCache.refStack}")
|
||||
return null
|
||||
}
|
||||
return builder(refCache[refId], refCache.stack(refId))
|
||||
}
|
||||
return builder(meta, refCache)
|
||||
}
|
||||
|
||||
internal fun <T : DObject> tObjectArray(
|
||||
builder: (Meta, DObjectCache) -> T
|
||||
): ReadOnlyProperty<Any?, List<T>> = ReadOnlyProperty { _, property ->
|
||||
meta.getIndexed(Name.of(property.name, "arr")).values.mapNotNull {
|
||||
resolve(builder, it)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun <T : DObject> dObject(
|
||||
builder: (Meta, DObjectCache) -> T,
|
||||
key: Name? = null
|
||||
): ReadOnlyProperty<Any?, T?> = ReadOnlyProperty { _, property ->
|
||||
meta[key ?: property.name.asName()]?.let { resolve(builder, it) }
|
||||
}
|
||||
}
|
||||
|
||||
public open class DNamed(meta: Meta, refCache: DObjectCache) : DObject(meta, refCache) {
|
||||
public val fName: String by meta.string("")
|
||||
public val fTitle: String by meta.string("")
|
||||
}
|
||||
|
||||
public class DGeoMaterial(meta: Meta, refCache: DObjectCache) : DNamed(meta, refCache) {
|
||||
}
|
||||
|
||||
public class DGeoMedium(meta: Meta, refCache: DObjectCache) : DNamed(meta, refCache) {
|
||||
public val fMaterial: DGeoMaterial? by dObject(::DGeoMaterial)
|
||||
public val fParams: DoubleArray by meta.doubleArray()
|
||||
}
|
||||
|
||||
public class DGeoShape(meta: Meta, refCache: DObjectCache) : DNamed(meta, refCache) {
|
||||
public val fDX: Double by meta.double(0.0)
|
||||
public val fDY: Double by meta.double(0.0)
|
||||
public val fDZ: Double by meta.double(0.0)
|
||||
}
|
||||
|
||||
public class DGeoVolume(meta: Meta, refCache: DObjectCache) : DNamed(meta, refCache), Named {
|
||||
public val fNodes: List<DGeoNode> by tObjectArray(::DGeoNode)
|
||||
public val fShape: DGeoShape? by dObject(::DGeoShape)
|
||||
public val fMedium: DGeoMedium? by dObject(::DGeoMedium)
|
||||
|
||||
override val name: Name by lazy { Name.parse(fName.ifEmpty { "volume[${meta.hashCode().toUInt()}]" }) }
|
||||
}
|
||||
|
||||
public class DGeoNode(meta: Meta, refCache: DObjectCache) : DNamed(meta, refCache) {
|
||||
public val fVolume: DGeoVolume? by dObject(::DGeoVolume)
|
||||
}
|
||||
|
||||
public class DGeoMatrix(meta: Meta, refCache: DObjectCache) : DNamed(meta, refCache) {
|
||||
}
|
||||
|
||||
|
||||
public class DGeoBoolNode(meta: Meta, refCache: DObjectCache) : DObject(meta, refCache) {
|
||||
public val fLeft: DGeoShape? by dObject(::DGeoShape)
|
||||
public val fLeftMat: DGeoMatrix? by dObject(::DGeoMatrix)
|
||||
|
||||
public val fRight: DGeoShape? by dObject(::DGeoShape)
|
||||
public val fRightMat: DGeoMatrix? by dObject(::DGeoMatrix)
|
||||
}
|
||||
|
||||
|
||||
public class DGeoManager(meta: Meta, refCache: DObjectCache) : DNamed(meta, refCache) {
|
||||
public val fMatrices: List<DGeoMatrix> by tObjectArray(::DGeoMatrix)
|
||||
|
||||
public val fShapes: List<DGeoShape> by tObjectArray(::DGeoShape)
|
||||
|
||||
public val fVolumes: List<DGeoVolume> by tObjectArray(::DGeoVolume)
|
||||
|
||||
public val fNodes: List<DGeoNode> by tObjectArray(::DGeoNode)
|
||||
|
||||
public companion object {
|
||||
|
||||
public fun parse(string: String): DGeoManager {
|
||||
val meta = Json.decodeFromString(MetaSerializer, string)
|
||||
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)
|
||||
|
||||
val refCache = DObjectCache(res)
|
||||
return DGeoManager(meta, refCache)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,140 +0,0 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,280 @@
|
||||
package ru.mipt.npm.root
|
||||
|
||||
import space.kscience.dataforge.meta.double
|
||||
import space.kscience.dataforge.meta.get
|
||||
import space.kscience.dataforge.meta.int
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.plus
|
||||
import space.kscience.visionforge.solid.*
|
||||
import kotlin.math.*
|
||||
|
||||
private val volumesName = Name.EMPTY //"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 RootToSolidContext(val prototypeHolder: PrototypeHolder)
|
||||
|
||||
// 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: DGeoMatrix?) {
|
||||
if (matrix == null) return
|
||||
when (matrix.typename) {
|
||||
"TGeoIdentity" -> {
|
||||
//do nothing
|
||||
}
|
||||
"TGeoTranslation" -> {
|
||||
val fTranslation by matrix.meta.doubleArray()
|
||||
translate(fTranslation)
|
||||
}
|
||||
"TGeoRotation" -> {
|
||||
val fRotationMatrix by matrix.meta.doubleArray()
|
||||
rotate(fRotationMatrix)
|
||||
}
|
||||
"TGeoCombiTrans" -> {
|
||||
val fTranslation by matrix.meta.doubleArray()
|
||||
|
||||
translate(fTranslation)
|
||||
if (matrix.meta["fRotationMatrix"] != null) {
|
||||
val fRotationMatrix by matrix.meta.doubleArray()
|
||||
rotate(fRotationMatrix)
|
||||
}
|
||||
}
|
||||
"TGeoHMatrix" -> {
|
||||
val fTranslation by matrix.meta.doubleArray()
|
||||
val fRotationMatrix by matrix.meta.doubleArray()
|
||||
val fScale by matrix.meta.doubleArray()
|
||||
translate(fTranslation)
|
||||
rotate(fRotationMatrix)
|
||||
scale = Point3D(fScale[0], fScale[1], fScale[2])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun SolidGroup.addShape(
|
||||
shape: DGeoShape,
|
||||
context: RootToSolidContext,
|
||||
name: String? = shape.fName.ifEmpty { null }
|
||||
): Solid? = when (shape.typename) {
|
||||
"TGeoCompositeShape" -> {
|
||||
val fNode: DGeoBoolNode? by shape.dObject(::DGeoBoolNode)
|
||||
val node = fNode ?: error("Composite shape node not resolved")
|
||||
val compositeType = when (node.typename) {
|
||||
"TGeoIntersection" -> CompositeType.INTERSECT
|
||||
"TGeoSubtraction" -> CompositeType.SUBTRACT
|
||||
"TGeoUnion" -> CompositeType.UNION
|
||||
else -> error("Unknown bool node type ${node.typename}")
|
||||
}
|
||||
composite(compositeType, name = name) {
|
||||
addShape(node.fLeft!!, context, "left").also {
|
||||
if (it == null) TODO()
|
||||
it.useMatrix(node.fLeftMat)
|
||||
}
|
||||
addShape(node.fRight!!, context, "right").also {
|
||||
if (it == null) TODO()
|
||||
it.useMatrix(node.fRightMat)
|
||||
}
|
||||
}
|
||||
}
|
||||
"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 = name) {
|
||||
(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 = name
|
||||
)
|
||||
}
|
||||
"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 = name
|
||||
)
|
||||
}
|
||||
"TGeoPcon" -> {
|
||||
val fDphi by shape.meta.double(0.0)
|
||||
val fNz by shape.meta.int(2)
|
||||
val fPhi1 by shape.meta.double(360.0)
|
||||
val fRmax by shape.meta.doubleArray()
|
||||
val fRmin by shape.meta.doubleArray()
|
||||
val fZ by shape.meta.doubleArray()
|
||||
if (fNz == 2) {
|
||||
coneSurface(
|
||||
bottomOuterRadius = fRmax[0],
|
||||
bottomInnerRadius = fRmin[0],
|
||||
height = fZ[1] - fZ[0],
|
||||
topOuterRadius = fRmax[1],
|
||||
topInnerRadius = fRmin[1],
|
||||
startAngle = degToRad(fPhi1),
|
||||
angle = degToRad(fDphi),
|
||||
name = name
|
||||
) {
|
||||
z = (fZ[1] + fZ[0]) / 2
|
||||
}
|
||||
} else {
|
||||
TODO()
|
||||
}
|
||||
}
|
||||
"TGeoPgon" -> {
|
||||
val fDphi by shape.meta.double(0.0)
|
||||
val fNz by shape.meta.int(2)
|
||||
val fPhi1 by shape.meta.double(360.0)
|
||||
val fRmax by shape.meta.doubleArray()
|
||||
val fRmin by shape.meta.doubleArray()
|
||||
val fZ by shape.meta.doubleArray()
|
||||
|
||||
val fNedges by shape.meta.int(1)
|
||||
|
||||
val startphi = degToRad(fPhi1)
|
||||
val deltaphi = degToRad(fDphi)
|
||||
|
||||
extruded(name) {
|
||||
//getting the radius of first
|
||||
require(fNz > 1) { "The polyhedron geometry requires at least two planes" }
|
||||
val baseRadius = fRmax[0]
|
||||
shape {
|
||||
(0..fNedges).forEach {
|
||||
val phi = deltaphi * fNedges * it + startphi
|
||||
(baseRadius * cos(phi) to baseRadius * sin(phi))
|
||||
}
|
||||
}
|
||||
(0 until fNz).forEach { index ->
|
||||
//scaling all radii relative to first layer radius
|
||||
layer(fZ[index], scale = fRmax[index] / baseRadius)
|
||||
}
|
||||
}
|
||||
}
|
||||
"TGeoShapeAssembly" -> {
|
||||
val fVolume by shape.dObject(::DGeoVolume)
|
||||
fVolume?.let { volume ->
|
||||
addRootVolume(volume, context)
|
||||
}
|
||||
}
|
||||
"TGeoBBox" -> {
|
||||
box(shape.fDX * 2, shape.fDY * 2, shape.fDZ * 2, name = name)
|
||||
}
|
||||
else -> {
|
||||
TODO("A shape with type ${shape.typename} not implemented")
|
||||
}
|
||||
}
|
||||
|
||||
private fun SolidGroup.addRootNode(obj: DGeoNode, context: RootToSolidContext) {
|
||||
val volume = obj.fVolume ?: return
|
||||
addRootVolume(volume, context, obj.fName).apply {
|
||||
when (obj.typename) {
|
||||
"TGeoNodeMatrix" -> {
|
||||
val fMatrix by obj.dObject(::DGeoMatrix)
|
||||
useMatrix(fMatrix)
|
||||
}
|
||||
"TGeoNodeOffset" -> {
|
||||
val fOffset by obj.meta.double(0.0)
|
||||
x = fOffset
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildGroup(volume: DGeoVolume, context: RootToSolidContext): SolidGroup = SolidGroup {
|
||||
volume.fShape?.let {
|
||||
addShape(it, context)
|
||||
}
|
||||
volume.fNodes.let {
|
||||
it.forEach { node ->
|
||||
addRootNode(node, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//private val SolidGroup.rootPrototypes: SolidGroup get() = (parent as? SolidGroup)?.rootPrototypes ?: this
|
||||
|
||||
private fun SolidGroup.addRootVolume(
|
||||
volume: DGeoVolume,
|
||||
context: RootToSolidContext,
|
||||
name: String? = null,
|
||||
cache: Boolean = true
|
||||
): Solid {
|
||||
val combinedName = if (volume.fName.isEmpty()) {
|
||||
name
|
||||
} else if (name == null) {
|
||||
volume.fName
|
||||
} else {
|
||||
"${name}_${volume.fName}"
|
||||
}
|
||||
|
||||
return if (!cache) {
|
||||
val group = buildGroup(volume, context)
|
||||
set(combinedName?.let { Name.parse(it) }, group)
|
||||
group
|
||||
} else {
|
||||
val templateName = volumesName + volume.name
|
||||
val existing = getPrototype(templateName)
|
||||
if (existing == null) {
|
||||
context.prototypeHolder.prototypes {
|
||||
set(templateName, buildGroup(volume, context))
|
||||
}
|
||||
}
|
||||
|
||||
return ref(templateName, name)
|
||||
}
|
||||
}
|
||||
|
||||
public fun DGeoManager.toSolid(): SolidGroup = SolidGroup {
|
||||
val context = RootToSolidContext(this)
|
||||
fNodes.forEach { node ->
|
||||
addRootNode(node, context)
|
||||
}
|
||||
}
|
@ -1,217 +0,0 @@
|
||||
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)
|
||||
}
|
||||
}
|
@ -5,8 +5,14 @@ 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.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.get
|
||||
import space.kscience.dataforge.meta.isLeaf
|
||||
import space.kscience.dataforge.values.string
|
||||
import space.kscience.visionforge.solid.Solids
|
||||
import java.nio.file.Paths
|
||||
import java.time.Duration
|
||||
import kotlin.io.path.writeText
|
||||
import kotlin.system.measureTimeMillis
|
||||
|
||||
private fun JsonElement.countTypes(): Sequence<String> = sequence {
|
||||
@ -25,14 +31,28 @@ private fun JsonElement.countTypes(): Sequence<String> = sequence {
|
||||
}
|
||||
}
|
||||
|
||||
private fun Meta.countTypes() :Sequence<String> = sequence {
|
||||
if(!isLeaf){
|
||||
get("_typename")?.value?.let { yield(it.string) }
|
||||
items.forEach { yieldAll(it.value.countTypes()) }
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val string = TGeoManager::class.java.getResourceAsStream("/BM@N.root.json")!!
|
||||
.readAllBytes().decodeToString()
|
||||
val time = measureTimeMillis {
|
||||
val geo = TGeoManagerScheme.parse(string)
|
||||
val geo = DGeoManager.parse(string)
|
||||
|
||||
val sizes = geo.meta.countTypes().groupBy { it }.mapValues { it.value.size }
|
||||
sizes.forEach {
|
||||
println(it)
|
||||
}
|
||||
|
||||
val solid = geo.toSolid()
|
||||
|
||||
println(Solids.encodeToString(solid))
|
||||
Paths.get("BM@N.vf.json").writeText(Solids.encodeToString(solid))
|
||||
//println(Solids.encodeToString(solid))
|
||||
}
|
||||
|
||||
// val json = Json.parseToJsonElement(string)
|
||||
|
@ -5,6 +5,7 @@ import kotlinx.serialization.Serializable
|
||||
import space.kscience.dataforge.meta.update
|
||||
import space.kscience.visionforge.VisionBuilder
|
||||
import space.kscience.visionforge.VisionContainerBuilder
|
||||
import space.kscience.visionforge.VisionPropertyContainer
|
||||
import space.kscience.visionforge.set
|
||||
|
||||
public enum class CompositeType {
|
||||
@ -20,7 +21,7 @@ public class Composite(
|
||||
public val compositeType: CompositeType,
|
||||
public val first: Solid,
|
||||
public val second: Solid,
|
||||
) : SolidBase(), Solid
|
||||
) : SolidBase(), VisionPropertyContainer<Composite>
|
||||
|
||||
@VisionBuilder
|
||||
public inline fun VisionContainerBuilder<Solid>.composite(
|
||||
@ -30,7 +31,7 @@ public inline fun VisionContainerBuilder<Solid>.composite(
|
||||
): Composite {
|
||||
val group = SolidGroup().apply(builder)
|
||||
val children = group.children.values.filterIsInstance<Solid>()
|
||||
if (children.size != 2) error("Composite requires exactly two children")
|
||||
if (children.size != 2) error("Composite requires exactly two children, but found ${children.size}")
|
||||
val res = Composite(type, children[0], children[1])
|
||||
|
||||
res.meta.update(group.meta)
|
||||
|
@ -165,7 +165,7 @@ public fun SolidGroup.ref(
|
||||
|
||||
/**
|
||||
* Add new [SolidReferenceGroup] wrapping given object and automatically adding it to the prototypes.
|
||||
* One must ensure that [prototypeHolder] is the owner of this group.
|
||||
* One must ensure that [prototypeHolder] is a parent of this group.
|
||||
*/
|
||||
public fun SolidGroup.newRef(
|
||||
name: String?,
|
||||
|
Loading…
Reference in New Issue
Block a user