Fix GDML transformation

This commit is contained in:
Alexander Nozik 2020-11-13 19:19:06 +03:00
parent 62a76f2c86
commit 613624ff17
3 changed files with 81 additions and 85 deletions

View File

@ -19,59 +19,66 @@ import kotlin.random.Random
private val solidsName = "solids".asName() private val solidsName = "solids".asName()
private val volumesName = "volumes".asName() private val volumesName = "volumes".asName()
public class GDMLTransformer internal constructor(public val root: GDML) { @Suppress("NOTHING_TO_INLINE")
//private val materialCache = HashMap<GDMLMaterial, Meta>() private inline operator fun Number.times(d: Double) = toDouble() * d
private val random = Random(222)
@Suppress("NOTHING_TO_INLINE")
private inline operator fun Number.times(f: Float) = toFloat() * f
public class GDMLTransformerSettings {
public enum class Action { public enum class Action {
ADD, ADD,
REJECT, REJECT,
PROTOTYPE PROTOTYPE
} }
public var lUnit: LUnit = LUnit.MM public var lUnit: LUnit = LUnit.CM
public var aUnit: AUnit = AUnit.RADIAN public var aUnit: AUnit = AUnit.RADIAN
public var solidAction: (GDMLSolid) -> Action = { Action.PROTOTYPE } public var solidAction: (GDMLSolid) -> Action = { Action.PROTOTYPE }
public var volumeAction: (GDMLGroup) -> Action = { Action.PROTOTYPE } public var volumeAction: (GDMLGroup) -> Action = { Action.PROTOTYPE }
}
private class GDMLTransformer(val settings: GDMLTransformerSettings) {
//private val materialCache = HashMap<GDMLMaterial, Meta>()
private val random = Random(222)
/** /**
* A special group for local templates * A special group for local templates
*/ */
private val proto by lazy { SolidGroup() } private val proto = SolidGroup()
private val solids by lazy { private val solids = proto.group(solidsName) {
proto.group(solidsName) {
config["edges.enabled"] = false config["edges.enabled"] = false
} }
}
private val referenceStore = HashMap<Name, MutableList<Proxy>>() private val referenceStore = HashMap<Name, MutableList<Proxy>>()
private fun proxySolid(group: SolidGroup, solid: GDMLSolid, name: String): Proxy { private fun proxySolid(root: GDML, group: SolidGroup, solid: GDMLSolid, name: String): Proxy {
val templateName = solidsName + name val templateName = solidsName + name
if (proto[templateName] == null) { if (proto[templateName] == null) {
solids.addSolid(solid, name) solids.addSolid(root, solid, name)
} }
val ref = group.ref(templateName, name) val ref = group.ref(templateName, name)
referenceStore.getOrPut(templateName) { ArrayList() }.add(ref) referenceStore.getOrPut(templateName) { ArrayList() }.add(ref)
return ref return ref
} }
private fun proxyVolume(group: SolidGroup, physVolume: GDMLPhysVolume, volume: GDMLGroup): Proxy { private fun proxyVolume(root: GDML, group: SolidGroup, physVolume: GDMLPhysVolume, volume: GDMLGroup): Proxy {
val templateName = volumesName + volume.name.asName() val templateName = volumesName + volume.name.asName()
if (proto[templateName] == null) { if (proto[templateName] == null) {
proto[templateName] = volume(volume) proto[templateName] = volume(root, volume)
} }
val ref = group.ref(templateName, physVolume.name ?: "").withPosition(physVolume) val ref = group.ref(templateName, physVolume.name ?: "").withPosition(root, physVolume)
referenceStore.getOrPut(templateName) { ArrayList() }.add(ref) referenceStore.getOrPut(templateName) { ArrayList() }.add(ref)
return ref return ref
} }
private val styleCache = HashMap<Name, Meta>() private val styleCache = HashMap<Name, Meta>()
public var solidConfiguration: Solid.(parent: GDMLVolume, solid: GDMLSolid) -> Unit = { parent, _ -> var solidConfiguration: Solid.(parent: GDMLVolume, solid: GDMLSolid) -> Unit = { parent, _ ->
lUnit = LUnit.CM
if (parent.physVolumes.isNotEmpty()) { if (parent.physVolumes.isNotEmpty()) {
useStyle("opaque") { useStyle("opaque") {
SolidMaterial.MATERIAL_OPACITY_KEY put 0.3 SolidMaterial.MATERIAL_OPACITY_KEY put 0.3
@ -80,14 +87,14 @@ public class GDMLTransformer internal constructor(public val root: GDML) {
} }
} }
private fun Solid.useStyle(name: String, builder: MetaBuilder.() -> Unit) { fun Solid.useStyle(name: String, builder: MetaBuilder.() -> Unit) {
styleCache.getOrPut(name.toName()) { styleCache.getOrPut(name.toName()) {
Meta(builder) Meta(builder)
} }
useStyle(name) useStyle(name)
} }
private fun configureSolid(obj: Solid, parent: GDMLVolume, solid: GDMLSolid) { fun configureSolid(root: GDML, obj: Solid, parent: GDMLVolume, solid: GDMLSolid) {
val material = parent.materialref.resolve(root) ?: GDMLElement(parent.materialref.ref) val material = parent.materialref.resolve(root) ?: GDMLElement(parent.materialref.ref)
val styleName = "material[${material.name}]" val styleName = "material[${material.name}]"
@ -100,22 +107,19 @@ public class GDMLTransformer internal constructor(public val root: GDML) {
obj.solidConfiguration(parent, solid) obj.solidConfiguration(parent, solid)
} }
public var onFinish: GDMLTransformer.() -> Unit = {} fun <T : Solid> T.withPosition(
private fun <T : Solid> T.withPosition(
newPos: GDMLPosition? = null, newPos: GDMLPosition? = null,
newRotation: GDMLRotation? = null, newRotation: GDMLRotation? = null,
newScale: GDMLScale? = null newScale: GDMLScale? = null,
): T = apply { ): T = apply {
newPos?.let { newPos?.let {
val point = Point3D(it.x(lUnit), it.y(lUnit), it.z(lUnit)) val point = Point3D(it.x(settings.lUnit), it.y(settings.lUnit), it.z(settings.lUnit))
if (position != null || point != World.ZERO) { if (position != null || point != World.ZERO) {
position = point position = point
} }
} }
newRotation?.let { newRotation?.let {
val point = Point3D(it.x(aUnit), it.y(aUnit), it.z(aUnit)) val point = Point3D(it.x(settings.aUnit), it.y(settings.aUnit), it.z(settings.aUnit))
if (rotation != null || point != World.ZERO) { if (rotation != null || point != World.ZERO) {
rotation = point rotation = point
} }
@ -130,24 +134,19 @@ public class GDMLTransformer internal constructor(public val root: GDML) {
//TODO convert units if needed //TODO convert units if needed
} }
private fun <T : Solid> T.withPosition(physVolume: GDMLPhysVolume): T = withPosition( fun <T : Solid> T.withPosition(root: GDML, physVolume: GDMLPhysVolume): T = withPosition(
physVolume.resolvePosition(root), physVolume.resolvePosition(root),
physVolume.resolveRotation(root), physVolume.resolveRotation(root),
physVolume.resolveScale(root) physVolume.resolveScale(root)
) )
@Suppress("NOTHING_TO_INLINE") fun SolidGroup.addSolid(
private inline operator fun Number.times(d: Double) = toDouble() * d root: GDML,
@Suppress("NOTHING_TO_INLINE")
private inline operator fun Number.times(f: Float) = toFloat() * f
private fun SolidGroup.addSolid(
solid: GDMLSolid, solid: GDMLSolid,
name: String = "" name: String = "",
): Solid { ): Solid {
//context.solidAdded(solid) //context.solidAdded(solid)
val lScale = solid.lscale(lUnit) val lScale = solid.lscale(settings.lUnit)
val aScale = solid.ascale() val aScale = solid.ascale()
return when (solid) { return when (solid) {
is GDMLBox -> box(solid.x * lScale, solid.y * lScale, solid.z * lScale, name) is GDMLBox -> box(solid.x * lScale, solid.y * lScale, solid.z * lScale, name)
@ -185,7 +184,7 @@ public class GDMLTransformer internal constructor(public val root: GDML) {
val innerSolid: GDMLSolid = solid.solidref.resolve(root) val innerSolid: GDMLSolid = solid.solidref.resolve(root)
?: error("Solid with tag ${solid.solidref.ref} for scaled solid ${solid.name} not defined") ?: error("Solid with tag ${solid.solidref.ref} for scaled solid ${solid.name} not defined")
addSolid(innerSolid, name).apply { addSolid(root, innerSolid, name).apply {
scaleX *= solid.scale.x.toFloat() scaleX *= solid.scale.x.toFloat()
scaleY *= solid.scale.y.toFloat() scaleY *= solid.scale.y.toFloat()
scaleZ = solid.scale.z.toFloat() scaleZ = solid.scale.z.toFloat()
@ -221,13 +220,13 @@ public class GDMLTransformer internal constructor(public val root: GDML) {
} }
return composite(type, name) { return composite(type, name) {
addSolid(first).withPosition( addSolid(root, first).withPosition(
solid.resolveFirstPosition(root), solid.resolveFirstPosition(root),
solid.resolveFirstRotation(root), solid.resolveFirstRotation(root),
null null
) )
addSolid(second).withPosition( addSolid(root, second).withPosition(
solid.resolvePosition(root), solid.resolvePosition(root),
solid.resolveRotation(root), solid.resolveRotation(root),
null null
@ -239,26 +238,28 @@ public class GDMLTransformer internal constructor(public val root: GDML) {
} }
} }
private fun SolidGroup.addSolidWithCaching( fun SolidGroup.addSolidWithCaching(
root: GDML,
solid: GDMLSolid, solid: GDMLSolid,
name: String = solid.name name: String = solid.name,
): Solid? { ): Solid? {
return when (solidAction(solid)) { return when (settings.solidAction(solid)) {
Action.ADD -> { GDMLTransformerSettings.Action.ADD -> {
addSolid(solid, name) addSolid(root, solid, name)
} }
Action.PROTOTYPE -> { GDMLTransformerSettings.Action.PROTOTYPE -> {
proxySolid(this, solid, name) proxySolid(root, this, solid, name)
} }
Action.REJECT -> { GDMLTransformerSettings.Action.REJECT -> {
//ignore //ignore
null null
} }
} }
} }
private fun SolidGroup.addPhysicalVolume( fun SolidGroup.addPhysicalVolume(
physVolume: GDMLPhysVolume root: GDML,
physVolume: GDMLPhysVolume,
) { ) {
val volume: GDMLGroup = physVolume.volumeref.resolve(root) val volume: GDMLGroup = physVolume.volumeref.resolve(root)
?: error("Volume with ref ${physVolume.volumeref.ref} could not be resolved") ?: error("Volume with ref ${physVolume.volumeref.ref} could not be resolved")
@ -267,60 +268,62 @@ public class GDMLTransformer internal constructor(public val root: GDML) {
if (volume is GDMLVolume && volume.physVolumes.isEmpty() && volume.placement == null) { if (volume is GDMLVolume && volume.physVolumes.isEmpty() && volume.placement == null) {
val solid = volume.solidref.resolve(root) val solid = volume.solidref.resolve(root)
?: error("Solid with tag ${volume.solidref.ref} for volume ${volume.name} not defined") ?: error("Solid with tag ${volume.solidref.ref} for volume ${volume.name} not defined")
addSolidWithCaching(solid, physVolume.name ?: "")?.apply { addSolidWithCaching(root, solid, physVolume.name ?: "")?.apply {
configureSolid(this, volume, solid) configureSolid(root, this, volume, solid)
withPosition(physVolume) withPosition(root, physVolume)
} }
return return
} }
when (volumeAction(volume)) { when (settings.volumeAction(volume)) {
Action.ADD -> { GDMLTransformerSettings.Action.ADD -> {
val group: SolidGroup = volume(volume) val group: SolidGroup = volume(root, volume)
this[physVolume.name ?: ""] = group.withPosition(physVolume) this[physVolume.name ?: ""] = group.withPosition(root, physVolume)
} }
Action.PROTOTYPE -> { GDMLTransformerSettings.Action.PROTOTYPE -> {
proxyVolume(this, physVolume, volume) proxyVolume(root, this, physVolume, volume)
} }
Action.REJECT -> { GDMLTransformerSettings.Action.REJECT -> {
//ignore //ignore
} }
} }
} }
private fun SolidGroup.addDivisionVolume( fun SolidGroup.addDivisionVolume(
divisionVolume: GDMLDivisionVolume root: GDML,
divisionVolume: GDMLDivisionVolume,
) { ) {
val volume: GDMLGroup = divisionVolume.volumeref.resolve(root) val volume: GDMLGroup = divisionVolume.volumeref.resolve(root)
?: error("Volume with ref ${divisionVolume.volumeref.ref} could not be resolved") ?: error("Volume with ref ${divisionVolume.volumeref.ref} could not be resolved")
//TODO add divisions //TODO add divisions
set(Name.EMPTY, volume(volume)) set(Name.EMPTY, volume(root, volume))
} }
private fun volume( private fun volume(
group: GDMLGroup root: GDML,
group: GDMLGroup,
): SolidGroup = SolidGroup().apply { ): SolidGroup = SolidGroup().apply {
if (group is GDMLVolume) { if (group is GDMLVolume) {
val solid: GDMLSolid = group.solidref.resolve(root) val solid: GDMLSolid = group.solidref.resolve(root)
?: error("Solid with tag ${group.solidref.ref} for volume ${group.name} not defined") ?: error("Solid with tag ${group.solidref.ref} for volume ${group.name} not defined")
addSolidWithCaching(solid)?.apply { addSolidWithCaching(root, solid)?.apply {
configureSolid(this, group, solid) configureSolid(root, this, group, solid)
} }
when (val vol: GDMLPlacement? = group.placement) { when (val vol: GDMLPlacement? = group.placement) {
is GDMLPhysVolume -> addPhysicalVolume(vol) is GDMLPhysVolume -> addPhysicalVolume(root, vol)
is GDMLDivisionVolume -> addDivisionVolume(vol) is GDMLDivisionVolume -> addDivisionVolume(root, vol)
} }
} }
group.physVolumes.forEach { physVolume -> group.physVolumes.forEach { physVolume ->
addPhysicalVolume(physVolume) addPhysicalVolume(root, physVolume)
} }
} }
private fun finalize(final: SolidGroup): SolidGroup { fun finalize(final: SolidGroup): SolidGroup {
//final.prototypes = proto //final.prototypes = proto
final.useStyle("GDML") { final.useStyle("GDML") {
Solid.ROTATION_ORDER_KEY put RotationOrder.ZXY Solid.ROTATION_ORDER_KEY put RotationOrder.ZXY
@ -351,25 +354,22 @@ public class GDMLTransformer internal constructor(public val root: GDML) {
define(it.key.toString(), it.value) define(it.key.toString(), it.value)
} }
} }
onFinish(this@GDMLTransformer)
return final return final
} }
public val result: SolidGroup by lazy { fun transform(root: GDML): SolidGroup = finalize(volume(root, root.world))
finalize(volume(root.world))
}
} }
public fun GDML.toVision(block: GDMLTransformer.() -> Unit = {}): SolidGroup { public fun GDML.toVision(block: GDMLTransformerSettings.() -> Unit = {}): SolidGroup {
val context = GDMLTransformer(this).apply(block) val context = GDMLTransformer(GDMLTransformerSettings().apply(block))
return context.result return context.transform(this)
} }
/** /**
* Append gdml node to the group * Append gdml node to the group
*/ */
public fun SolidGroup.gdml(gdml: GDML, key: String = "", transformer: GDMLTransformer.() -> Unit = {}) { public fun SolidGroup.gdml(gdml: GDML, key: String = "", transformer: GDMLTransformerSettings.() -> Unit = {}) {
val visual = gdml.toVision(transformer) val visual = gdml.toVision(transformer)
//println(Visual3DPlugin.json.stringify(VisualGroup3D.serializer(), visual)) //println(Visual3DPlugin.json.stringify(VisualGroup3D.serializer(), visual))
set(key, visual) set(key, visual)

View File

@ -1,20 +1,20 @@
package hep.dataforge.vision.gdml package hep.dataforge.vision.gdml
import hep.dataforge.vision.solid.SolidGroup import hep.dataforge.vision.solid.SolidGroup
import nl.adaptivity.xmlutil.StAXReader
import kscience.gdml.GDML import kscience.gdml.GDML
import nl.adaptivity.xmlutil.StAXReader
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
public actual typealias Counter = AtomicInteger public actual typealias Counter = AtomicInteger
fun GDML.Companion.readFile(file: Path): GDML { public fun GDML.Companion.readFile(file: Path): GDML {
val xmlReader = StAXReader(Files.newInputStream(file), "UTF-8") val xmlReader = StAXReader(Files.newInputStream(file), "UTF-8")
return format.parse(GDML.serializer(), xmlReader) return format.parse(GDML.serializer(), xmlReader)
} }
fun SolidGroup.gdml(file: Path, key: String = "", transformer: GDMLTransformer.() -> Unit = {}) { public fun SolidGroup.gdml(file: Path, key: String = "", transformer: GDMLTransformerSettings.() -> Unit = {}) {
val gdml = GDML.readFile(file) val gdml = GDML.readFile(file)
gdml(gdml, key, transformer) gdml(gdml, key, transformer)
} }

View File

@ -50,14 +50,10 @@ public abstract class AbstractProxy : BasicSolid(), VisionGroup {
*/ */
@Serializable @Serializable
@SerialName("solid.proxy") @SerialName("solid.proxy")
public class Proxy private constructor( public class Proxy(
public val templateName: Name, public val templateName: Name,
) : AbstractProxy(), Solid { ) : AbstractProxy(), Solid {
public constructor(parent: SolidGroup, templateName: Name) : this(templateName) {
this.parent = parent
}
/** /**
* Recursively search for defined template in the parent * Recursively search for defined template in the parent
*/ */
@ -145,7 +141,7 @@ public val Vision.prototype: Vision
public fun SolidGroup.ref( public fun SolidGroup.ref(
templateName: Name, templateName: Name,
name: String = "", name: String = "",
): Proxy = Proxy(this, templateName).also { set(name, it) } ): Proxy = Proxy(templateName).also { set(name, it) }
/** /**
* Add new proxy wrapping given object and automatically adding it to the prototypes * Add new proxy wrapping given object and automatically adding it to the prototypes