forked from kscience/visionforge
GDML converter refactoring
This commit is contained in:
parent
68cf4748d8
commit
c83a25b0a1
@ -7,6 +7,7 @@ import hep.dataforge.names.Name
|
|||||||
import hep.dataforge.names.asName
|
import hep.dataforge.names.asName
|
||||||
import hep.dataforge.names.plus
|
import hep.dataforge.names.plus
|
||||||
import hep.dataforge.names.toName
|
import hep.dataforge.names.toName
|
||||||
|
import hep.dataforge.vision.MutableVisionGroup
|
||||||
import hep.dataforge.vision.set
|
import hep.dataforge.vision.set
|
||||||
import hep.dataforge.vision.solid.*
|
import hep.dataforge.vision.solid.*
|
||||||
import hep.dataforge.vision.solid.SolidMaterial.Companion.MATERIAL_COLOR_KEY
|
import hep.dataforge.vision.solid.SolidMaterial.Companion.MATERIAL_COLOR_KEY
|
||||||
@ -19,7 +20,7 @@ import kotlin.random.Random
|
|||||||
private val solidsName = "solids".asName()
|
private val solidsName = "solids".asName()
|
||||||
private val volumesName = "volumes".asName()
|
private val volumesName = "volumes".asName()
|
||||||
|
|
||||||
class GDMLTransformer(val root: GDML) {
|
class GDMLTransformer internal constructor(val root: GDML) {
|
||||||
//private val materialCache = HashMap<GDMLMaterial, Meta>()
|
//private val materialCache = HashMap<GDMLMaterial, Meta>()
|
||||||
private val random = Random(222)
|
private val random = Random(222)
|
||||||
|
|
||||||
@ -38,25 +39,35 @@ class GDMLTransformer(val root: GDML) {
|
|||||||
/**
|
/**
|
||||||
* A special group for local templates
|
* A special group for local templates
|
||||||
*/
|
*/
|
||||||
internal val proto by lazy { SolidGroup() }
|
private val proto by lazy { SolidGroup() }
|
||||||
|
|
||||||
internal val solids by lazy {
|
private val solids by lazy {
|
||||||
proto.group(solidsName) {
|
proto.group(solidsName) {
|
||||||
config["edges.enabled"] = false
|
config["edges.enabled"] = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val volumes by lazy {
|
private val referenceStore = HashMap<Name, MutableList<Proxy>>()
|
||||||
proto.group(volumesName)
|
|
||||||
|
private fun proxySolid(group: SolidGroup, solid: GDMLSolid, name: String): Proxy {
|
||||||
|
val templateName = solidsName + name
|
||||||
|
if (proto[templateName] == null) {
|
||||||
|
solids.addSolid(solid, name)
|
||||||
|
}
|
||||||
|
val ref = group.ref(templateName, name)
|
||||||
|
referenceStore.getOrPut(templateName) { ArrayList() }.add(ref)
|
||||||
|
return ref
|
||||||
}
|
}
|
||||||
|
|
||||||
// fun proxySolid(group: SolidGroup, solid: GDMLSolid, name: String): Proxy {
|
private fun proxyVolume(group: SolidGroup, physVolume: GDMLPhysVolume, volume: GDMLGroup): Proxy {
|
||||||
// val fullName = solidsName + name
|
val templateName = volumesName + volume.name.asName()
|
||||||
// if (proto[fullName] == null) {
|
if (proto[templateName] == null) {
|
||||||
// solids.addSolid(this, solid, name)
|
proto[templateName] = volume(volume)
|
||||||
// }
|
}
|
||||||
// return group.ref(fullName, name)
|
val ref = group.ref(templateName, physVolume.name ?: "").withPosition(physVolume)
|
||||||
// }
|
referenceStore.getOrPut(templateName) { ArrayList() }.add(ref)
|
||||||
|
return ref
|
||||||
|
}
|
||||||
|
|
||||||
private val styleCache = HashMap<Name, Meta>()
|
private val styleCache = HashMap<Name, Meta>()
|
||||||
|
|
||||||
@ -70,14 +81,14 @@ class GDMLTransformer(val root: GDML) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Solid.useStyle(name: String, builder: MetaBuilder.() -> Unit) {
|
private fun Solid.useStyle(name: String, builder: MetaBuilder.() -> Unit) {
|
||||||
styleCache.getOrPut(name.toName()) {
|
styleCache.getOrPut(name.toName()) {
|
||||||
Meta(builder)
|
Meta(builder)
|
||||||
}
|
}
|
||||||
useStyle(name)
|
useStyle(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun configureSolid(obj: Solid, parent: GDMLVolume, solid: GDMLSolid) {
|
private fun configureSolid(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}]"
|
||||||
@ -92,8 +103,244 @@ class GDMLTransformer(val root: GDML) {
|
|||||||
|
|
||||||
var onFinish: GDMLTransformer.() -> Unit = {}
|
var onFinish: GDMLTransformer.() -> Unit = {}
|
||||||
|
|
||||||
internal fun finalize(final: SolidGroup): SolidGroup {
|
|
||||||
|
private fun <T : Solid> T.withPosition(
|
||||||
|
newPos: GDMLPosition? = null,
|
||||||
|
newRotation: GDMLRotation? = null,
|
||||||
|
newScale: GDMLScale? = null
|
||||||
|
): T = apply {
|
||||||
|
newPos?.let {
|
||||||
|
val point = Point3D(it.x(lUnit), it.y(lUnit), it.z(lUnit))
|
||||||
|
if (position != null || point != World.ZERO) {
|
||||||
|
position = point
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newRotation?.let {
|
||||||
|
val point = Point3D(it.x(aUnit), it.y(aUnit), it.z(aUnit))
|
||||||
|
if (rotation != null || point != World.ZERO) {
|
||||||
|
rotation = point
|
||||||
|
}
|
||||||
|
//this@withPosition.rotationOrder = RotationOrder.ZXY
|
||||||
|
}
|
||||||
|
newScale?.let {
|
||||||
|
val point = Point3D(it.x, it.y, it.z)
|
||||||
|
if (scale != null || point != World.ONE) {
|
||||||
|
scale = point
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//TODO convert units if needed
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <T : Solid> T.withPosition(physVolume: GDMLPhysVolume): T = withPosition(
|
||||||
|
physVolume.resolvePosition(root),
|
||||||
|
physVolume.resolveRotation(root),
|
||||||
|
physVolume.resolveScale(root)
|
||||||
|
)
|
||||||
|
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
private inline operator fun Number.times(d: Double) = toDouble() * d
|
||||||
|
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
private inline operator fun Number.times(f: Float) = toFloat() * f
|
||||||
|
|
||||||
|
private fun SolidGroup.addSolid(
|
||||||
|
solid: GDMLSolid,
|
||||||
|
name: String = ""
|
||||||
|
): Solid {
|
||||||
|
//context.solidAdded(solid)
|
||||||
|
val lScale = solid.lscale(lUnit)
|
||||||
|
val aScale = solid.ascale()
|
||||||
|
return when (solid) {
|
||||||
|
is GDMLBox -> box(solid.x * lScale, solid.y * lScale, solid.z * lScale, name)
|
||||||
|
is GDMLTube -> tube(
|
||||||
|
solid.rmax * lScale,
|
||||||
|
solid.z * lScale,
|
||||||
|
solid.rmin * lScale,
|
||||||
|
solid.startphi * aScale,
|
||||||
|
solid.deltaphi * aScale,
|
||||||
|
name
|
||||||
|
)
|
||||||
|
is GDMLCone -> cone(solid.rmax1, solid.z, solid.rmax2, name = name) {
|
||||||
|
require(solid.rmin1 == 0.0) { "Empty cones are not supported" }
|
||||||
|
require(solid.rmin2 == 0.0) { "Empty cones are not supported" }
|
||||||
|
startAngle = solid.startphi.toFloat()
|
||||||
|
angle = solid.deltaphi.toFloat()
|
||||||
|
}
|
||||||
|
is GDMLXtru -> extrude(name) {
|
||||||
|
shape {
|
||||||
|
solid.vertices.forEach {
|
||||||
|
point(it.x * lScale, it.y * lScale)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
solid.sections.sortedBy { it.zOrder }.forEach { section ->
|
||||||
|
layer(
|
||||||
|
section.zPosition * lScale,
|
||||||
|
section.xOffset * lScale,
|
||||||
|
section.yOffset * lScale,
|
||||||
|
section.scalingFactor
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is GDMLScaledSolid -> {
|
||||||
|
//Add solid with modified scale
|
||||||
|
val innerSolid: GDMLSolid = solid.solidref.resolve(root)
|
||||||
|
?: error("Solid with tag ${solid.solidref.ref} for scaled solid ${solid.name} not defined")
|
||||||
|
|
||||||
|
addSolid(innerSolid, name).apply {
|
||||||
|
scaleX *= solid.scale.x.toFloat()
|
||||||
|
scaleY *= solid.scale.y.toFloat()
|
||||||
|
scaleZ = solid.scale.z.toFloat()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is GDMLSphere -> sphere(solid.rmax * lScale, solid.deltaphi * aScale, solid.deltatheta * aScale, name) {
|
||||||
|
phiStart = solid.startphi * aScale
|
||||||
|
thetaStart = solid.starttheta * aScale
|
||||||
|
}
|
||||||
|
is GDMLOrb -> sphere(solid.r * lScale, name = name)
|
||||||
|
is GDMLPolyhedra -> extrude(name) {
|
||||||
|
//getting the radius of first
|
||||||
|
require(solid.planes.size > 1) { "The polyhedron geometry requires at least two planes" }
|
||||||
|
val baseRadius = solid.planes.first().rmax * lScale
|
||||||
|
shape {
|
||||||
|
(0..solid.numsides).forEach {
|
||||||
|
val phi = solid.deltaphi * aScale / solid.numsides * it + solid.startphi * aScale
|
||||||
|
(baseRadius * cos(phi) to baseRadius * sin(phi))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
solid.planes.forEach { plane ->
|
||||||
|
//scaling all radii relative to first layer radius
|
||||||
|
layer(plane.z * lScale, scale = plane.rmax * lScale / baseRadius)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is GDMLBoolSolid -> {
|
||||||
|
val first: GDMLSolid = solid.first.resolve(root) ?: error("")
|
||||||
|
val second: GDMLSolid = solid.second.resolve(root) ?: error("")
|
||||||
|
val type: CompositeType = when (solid) {
|
||||||
|
is GDMLUnion -> CompositeType.UNION
|
||||||
|
is GDMLSubtraction -> CompositeType.SUBTRACT
|
||||||
|
is GDMLIntersection -> CompositeType.INTERSECT
|
||||||
|
}
|
||||||
|
|
||||||
|
return composite(type, name) {
|
||||||
|
addSolid(first).withPosition(
|
||||||
|
solid.resolveFirstPosition(root),
|
||||||
|
solid.resolveFirstRotation(root),
|
||||||
|
null
|
||||||
|
)
|
||||||
|
|
||||||
|
addSolid(second).withPosition(
|
||||||
|
solid.resolvePosition(root),
|
||||||
|
solid.resolveRotation(root),
|
||||||
|
null
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> error("Renderer for $solid not supported yet")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun SolidGroup.addSolidWithCaching(
|
||||||
|
solid: GDMLSolid,
|
||||||
|
name: String = solid.name
|
||||||
|
): Solid? {
|
||||||
|
return when (solidAction(solid)) {
|
||||||
|
Action.ADD -> {
|
||||||
|
addSolid(solid, name)
|
||||||
|
}
|
||||||
|
Action.PROTOTYPE -> {
|
||||||
|
proxySolid(this, solid, name)
|
||||||
|
}
|
||||||
|
Action.REJECT -> {
|
||||||
|
//ignore
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun SolidGroup.addPhysicalVolume(
|
||||||
|
physVolume: GDMLPhysVolume
|
||||||
|
) {
|
||||||
|
val volume: GDMLGroup = physVolume.volumeref.resolve(root)
|
||||||
|
?: error("Volume with ref ${physVolume.volumeref.ref} could not be resolved")
|
||||||
|
|
||||||
|
// a special case for single solid volume
|
||||||
|
if (volume is GDMLVolume && volume.physVolumes.isEmpty() && volume.placement == null) {
|
||||||
|
val solid = volume.solidref.resolve(root)
|
||||||
|
?: error("Solid with tag ${volume.solidref.ref} for volume ${volume.name} not defined")
|
||||||
|
addSolidWithCaching(solid, physVolume.name ?: "")?.apply {
|
||||||
|
configureSolid(this, volume, solid)
|
||||||
|
withPosition(physVolume)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
when (volumeAction(volume)) {
|
||||||
|
Action.ADD -> {
|
||||||
|
val group: SolidGroup = volume(volume)
|
||||||
|
this[physVolume.name ?: ""] = group.withPosition(physVolume)
|
||||||
|
}
|
||||||
|
Action.PROTOTYPE -> {
|
||||||
|
proxyVolume(this, physVolume, volume)
|
||||||
|
}
|
||||||
|
Action.REJECT -> {
|
||||||
|
//ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun SolidGroup.addDivisionVolume(
|
||||||
|
divisionVolume: GDMLDivisionVolume
|
||||||
|
) {
|
||||||
|
val volume: GDMLGroup = divisionVolume.volumeref.resolve(root)
|
||||||
|
?: error("Volume with ref ${divisionVolume.volumeref.ref} could not be resolved")
|
||||||
|
|
||||||
|
//TODO add divisions
|
||||||
|
set(Name.EMPTY, volume(volume))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun volume(
|
||||||
|
group: GDMLGroup
|
||||||
|
): SolidGroup = SolidGroup().apply {
|
||||||
|
if (group is GDMLVolume) {
|
||||||
|
val solid: GDMLSolid = group.solidref.resolve(root)
|
||||||
|
?: error("Solid with tag ${group.solidref.ref} for volume ${group.name} not defined")
|
||||||
|
|
||||||
|
addSolidWithCaching(solid)?.apply {
|
||||||
|
configureSolid(this, group, solid)
|
||||||
|
}
|
||||||
|
|
||||||
|
when (val vol: GDMLPlacement? = group.placement) {
|
||||||
|
is GDMLPhysVolume -> addPhysicalVolume(vol)
|
||||||
|
is GDMLDivisionVolume -> addDivisionVolume(vol)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
group.physVolumes.forEach { physVolume ->
|
||||||
|
addPhysicalVolume(physVolume)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun finalize(final: SolidGroup): SolidGroup {
|
||||||
//final.prototypes = proto
|
//final.prototypes = proto
|
||||||
|
final.useStyle("GDML") {
|
||||||
|
Solid.ROTATION_ORDER_KEY put RotationOrder.ZXY
|
||||||
|
}
|
||||||
|
|
||||||
|
//inline prototypes
|
||||||
|
// referenceStore.forEach { (protoName, list) ->
|
||||||
|
// val proxy = list.singleOrNull() ?: return@forEach
|
||||||
|
// val parent = proxy.parent as? MutableVisionGroup ?: return@forEach
|
||||||
|
// val token = parent.children.entries.find { it.value == proxy }?.key ?: error("Inconsistent reference cache")
|
||||||
|
// val prototype = proto[protoName] as? Solid ?: error("Inconsistent reference cache")
|
||||||
|
// prototype.parent = null
|
||||||
|
// parent[token] = prototype
|
||||||
|
// prototype.updateFrom(proxy)
|
||||||
|
//
|
||||||
|
// //FIXME update prototype
|
||||||
|
// proto[protoName] = null
|
||||||
|
// }
|
||||||
|
|
||||||
final.prototypes {
|
final.prototypes {
|
||||||
proto.children.forEach { (token, item) ->
|
proto.children.forEach { (token, item) ->
|
||||||
item.parent = null
|
item.parent = null
|
||||||
@ -105,252 +352,19 @@ class GDMLTransformer(val root: GDML) {
|
|||||||
define(it.key.toString(), it.value)
|
define(it.key.toString(), it.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final.rotationOrder = RotationOrder.ZXY
|
|
||||||
onFinish(this@GDMLTransformer)
|
onFinish(this@GDMLTransformer)
|
||||||
return final
|
return final
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
val result by lazy {
|
||||||
|
finalize(volume(root.world))
|
||||||
private fun Solid.withPosition(
|
|
||||||
lUnit: LUnit,
|
|
||||||
aUnit: AUnit = AUnit.RADIAN,
|
|
||||||
newPos: GDMLPosition? = null,
|
|
||||||
newRotation: GDMLRotation? = null,
|
|
||||||
newScale: GDMLScale? = null
|
|
||||||
): Solid = apply {
|
|
||||||
newPos?.let {
|
|
||||||
val point = Point3D(it.x(lUnit), it.y(lUnit), it.z(lUnit))
|
|
||||||
if (position != null || point != World.ZERO) {
|
|
||||||
position = point
|
|
||||||
}
|
|
||||||
}
|
|
||||||
newRotation?.let {
|
|
||||||
val point = Point3D(it.x(aUnit), it.y(aUnit), it.z(aUnit))
|
|
||||||
if (rotation != null || point != World.ZERO) {
|
|
||||||
rotation = point
|
|
||||||
}
|
|
||||||
//this@withPosition.rotationOrder = RotationOrder.ZXY
|
|
||||||
}
|
|
||||||
newScale?.let {
|
|
||||||
val point = Point3D(it.x, it.y, it.z)
|
|
||||||
if (scale != null || point != World.ONE) {
|
|
||||||
scale = point
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//TODO convert units if needed
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun Solid.withPosition(context: GDMLTransformer, physVolume: GDMLPhysVolume) = withPosition(
|
|
||||||
context.lUnit, context.aUnit,
|
|
||||||
physVolume.resolvePosition(context.root),
|
|
||||||
physVolume.resolveRotation(context.root),
|
|
||||||
physVolume.resolveScale(context.root)
|
|
||||||
)
|
|
||||||
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
private inline operator fun Number.times(d: Double) = toDouble() * d
|
|
||||||
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
private inline operator fun Number.times(f: Float) = toFloat() * f
|
|
||||||
|
|
||||||
private fun SolidGroup.addSolid(
|
|
||||||
context: GDMLTransformer,
|
|
||||||
solid: GDMLSolid,
|
|
||||||
name: String = ""
|
|
||||||
): Solid {
|
|
||||||
//context.solidAdded(solid)
|
|
||||||
val lScale = solid.lscale(context.lUnit)
|
|
||||||
val aScale = solid.ascale()
|
|
||||||
return when (solid) {
|
|
||||||
is GDMLBox -> box(solid.x * lScale, solid.y * lScale, solid.z * lScale, name)
|
|
||||||
is GDMLTube -> tube(
|
|
||||||
solid.rmax * lScale,
|
|
||||||
solid.z * lScale,
|
|
||||||
solid.rmin * lScale,
|
|
||||||
solid.startphi * aScale,
|
|
||||||
solid.deltaphi * aScale,
|
|
||||||
name
|
|
||||||
)
|
|
||||||
is GDMLCone -> cone(solid.rmax1, solid.z, solid.rmax2, name = name) {
|
|
||||||
require(solid.rmin1 == 0.0) { "Empty cones are not supported" }
|
|
||||||
require(solid.rmin2 == 0.0) { "Empty cones are not supported" }
|
|
||||||
startAngle = solid.startphi.toFloat()
|
|
||||||
angle = solid.deltaphi.toFloat()
|
|
||||||
}
|
|
||||||
is GDMLXtru -> extrude(name) {
|
|
||||||
shape {
|
|
||||||
solid.vertices.forEach {
|
|
||||||
point(it.x * lScale, it.y * lScale)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
solid.sections.sortedBy { it.zOrder }.forEach { section ->
|
|
||||||
layer(
|
|
||||||
section.zPosition * lScale,
|
|
||||||
section.xOffset * lScale,
|
|
||||||
section.yOffset * lScale,
|
|
||||||
section.scalingFactor
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is GDMLScaledSolid -> {
|
|
||||||
//Add solid with modified scale
|
|
||||||
val innerSolid: GDMLSolid = solid.solidref.resolve(context.root)
|
|
||||||
?: error("Solid with tag ${solid.solidref.ref} for scaled solid ${solid.name} not defined")
|
|
||||||
|
|
||||||
addSolid(context, innerSolid, name).apply {
|
|
||||||
scaleX *= solid.scale.x.toFloat()
|
|
||||||
scaleY *= solid.scale.y.toFloat()
|
|
||||||
scaleZ = solid.scale.z.toFloat()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is GDMLSphere -> sphere(solid.rmax * lScale, solid.deltaphi * aScale, solid.deltatheta * aScale, name) {
|
|
||||||
phiStart = solid.startphi * aScale
|
|
||||||
thetaStart = solid.starttheta * aScale
|
|
||||||
}
|
|
||||||
is GDMLOrb -> sphere(solid.r * lScale, name = name)
|
|
||||||
is GDMLPolyhedra -> extrude(name) {
|
|
||||||
//getting the radius of first
|
|
||||||
require(solid.planes.size > 1) { "The polyhedron geometry requires at least two planes" }
|
|
||||||
val baseRadius = solid.planes.first().rmax * lScale
|
|
||||||
shape {
|
|
||||||
(0..solid.numsides).forEach {
|
|
||||||
val phi = solid.deltaphi * aScale / solid.numsides * it + solid.startphi * aScale
|
|
||||||
(baseRadius * cos(phi) to baseRadius * sin(phi))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
solid.planes.forEach { plane ->
|
|
||||||
//scaling all radii relative to first layer radius
|
|
||||||
layer(plane.z * lScale, scale = plane.rmax * lScale / baseRadius)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is GDMLBoolSolid -> {
|
|
||||||
val first: GDMLSolid = solid.first.resolve(context.root) ?: error("")
|
|
||||||
val second: GDMLSolid = solid.second.resolve(context.root) ?: error("")
|
|
||||||
val type: CompositeType = when (solid) {
|
|
||||||
is GDMLUnion -> CompositeType.UNION
|
|
||||||
is GDMLSubtraction -> CompositeType.SUBTRACT
|
|
||||||
is GDMLIntersection -> CompositeType.INTERSECT
|
|
||||||
}
|
|
||||||
|
|
||||||
return composite(type, name) {
|
|
||||||
addSolid(context, first).withPosition(
|
|
||||||
context.lUnit, context.aUnit,
|
|
||||||
solid.resolveFirstPosition(context.root),
|
|
||||||
solid.resolveFirstRotation(context.root),
|
|
||||||
null
|
|
||||||
)
|
|
||||||
|
|
||||||
addSolid(context, second).withPosition(
|
|
||||||
context.lUnit, context.aUnit,
|
|
||||||
solid.resolvePosition(context.root),
|
|
||||||
solid.resolveRotation(context.root),
|
|
||||||
null
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> error("Renderer for $solid not supported yet")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun SolidGroup.addSolidWithCaching(
|
|
||||||
context: GDMLTransformer,
|
|
||||||
solid: GDMLSolid,
|
|
||||||
name: String = solid.name
|
|
||||||
): Solid? {
|
|
||||||
return when (context.solidAction(solid)) {
|
|
||||||
GDMLTransformer.Action.ADD -> {
|
|
||||||
addSolid(context, solid, name)
|
|
||||||
}
|
|
||||||
GDMLTransformer.Action.PROTOTYPE -> {
|
|
||||||
// context.proxySolid(this, solid, name)
|
|
||||||
val fullName = solidsName + solid.name.asName()
|
|
||||||
if (context.proto[fullName] == null) {
|
|
||||||
context.solids.addSolid(context, solid, solid.name)
|
|
||||||
}
|
|
||||||
ref(fullName, name)
|
|
||||||
}
|
|
||||||
GDMLTransformer.Action.REJECT -> {
|
|
||||||
//ignore
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun SolidGroup.addPhysicalVolume(
|
|
||||||
context: GDMLTransformer,
|
|
||||||
physVolume: GDMLPhysVolume
|
|
||||||
) {
|
|
||||||
val volume: GDMLGroup = physVolume.volumeref.resolve(context.root)
|
|
||||||
?: error("Volume with ref ${physVolume.volumeref.ref} could not be resolved")
|
|
||||||
|
|
||||||
// a special case for single solid volume
|
|
||||||
if (volume is GDMLVolume && volume.physVolumes.isEmpty() && volume.placement == null) {
|
|
||||||
val solid = volume.solidref.resolve(context.root)
|
|
||||||
?: error("Solid with tag ${volume.solidref.ref} for volume ${volume.name} not defined")
|
|
||||||
addSolidWithCaching(context, solid, physVolume.name ?: "")?.apply {
|
|
||||||
context.configureSolid(this, volume, solid)
|
|
||||||
withPosition(context, physVolume)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
when (context.volumeAction(volume)) {
|
|
||||||
GDMLTransformer.Action.ADD -> {
|
|
||||||
val group: SolidGroup = volume(context, volume)
|
|
||||||
this[physVolume.name ?: ""] = group.withPosition(context, physVolume)
|
|
||||||
}
|
|
||||||
GDMLTransformer.Action.PROTOTYPE -> {
|
|
||||||
val fullName = volumesName + volume.name.asName()
|
|
||||||
if (context.proto[fullName] == null) {
|
|
||||||
context.proto[fullName] = volume(context, volume)
|
|
||||||
}
|
|
||||||
ref(fullName, physVolume.name ?: "").withPosition(context, physVolume)
|
|
||||||
}
|
|
||||||
GDMLTransformer.Action.REJECT -> {
|
|
||||||
//ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun SolidGroup.addDivisionVolume(
|
|
||||||
context: GDMLTransformer,
|
|
||||||
divisionVolume: GDMLDivisionVolume
|
|
||||||
) {
|
|
||||||
val volume: GDMLGroup = divisionVolume.volumeref.resolve(context.root)
|
|
||||||
?: error("Volume with ref ${divisionVolume.volumeref.ref} could not be resolved")
|
|
||||||
|
|
||||||
//TODO add divisions
|
|
||||||
set(Name.EMPTY, volume(context, volume))
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun volume(
|
|
||||||
context: GDMLTransformer,
|
|
||||||
group: GDMLGroup
|
|
||||||
): SolidGroup = SolidGroup().apply {
|
|
||||||
if (group is GDMLVolume) {
|
|
||||||
val solid: GDMLSolid = group.solidref.resolve(context.root)
|
|
||||||
?: error("Solid with tag ${group.solidref.ref} for volume ${group.name} not defined")
|
|
||||||
|
|
||||||
addSolidWithCaching(context, solid)?.apply {
|
|
||||||
context.configureSolid(this, group, solid)
|
|
||||||
}
|
|
||||||
|
|
||||||
when (val vol: GDMLPlacement? = group.placement) {
|
|
||||||
is GDMLPhysVolume -> addPhysicalVolume(context, vol)
|
|
||||||
is GDMLDivisionVolume -> addDivisionVolume(context, vol)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
group.physVolumes.forEach { physVolume ->
|
|
||||||
addPhysicalVolume(context, physVolume)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun GDML.toVision(block: GDMLTransformer.() -> Unit = {}): SolidGroup {
|
fun GDML.toVision(block: GDMLTransformer.() -> Unit = {}): SolidGroup {
|
||||||
val context = GDMLTransformer(this).apply(block)
|
val context = GDMLTransformer(this).apply(block)
|
||||||
return context.finalize(volume(context, world))
|
return context.result
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -18,72 +18,72 @@ expect class Counter() {
|
|||||||
fun incrementAndGet(): Int
|
fun incrementAndGet(): Int
|
||||||
}
|
}
|
||||||
|
|
||||||
@DFExperimental
|
private fun Point3D?.safePlus(other: Point3D?): Point3D? = if (this == null && other == null) {
|
||||||
private class GdmlOptimizer() : VisionVisitor {
|
null
|
||||||
val logger = KotlinLogging.logger("SingleChildReducer")
|
} else {
|
||||||
|
(this ?: Point3D(0, 0, 0)) + (other ?: Point3D(0, 0, 0))
|
||||||
private operator fun Point3D?.plus(other: Point3D?): Point3D? = if (this == null && other == null) {
|
|
||||||
null
|
|
||||||
} else {
|
|
||||||
(this ?: Point3D(0, 0, 0)) + (other ?: Point3D(0, 0, 0))
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun Vision.updateFrom(other: Vision): Vision {
|
|
||||||
if (this is Solid && other is Solid) {
|
|
||||||
position += other.position
|
|
||||||
rotation += other.rotation
|
|
||||||
if (this.scale != null || other.scale != null) {
|
|
||||||
scaleX = scaleX.toDouble() * other.scaleX.toDouble()
|
|
||||||
scaleY = scaleY.toDouble() * other.scaleY.toDouble()
|
|
||||||
scaleZ = scaleZ.toDouble() * other.scaleZ.toDouble()
|
|
||||||
}
|
|
||||||
other.properties?.sequence()?.forEach { (name, item) ->
|
|
||||||
if (properties?.getItem(name) == null) {
|
|
||||||
config[name] = item
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
private val depthCount = HashMap<Int, Counter>()
|
|
||||||
|
|
||||||
override suspend fun visit(name: Name, vision: Vision) {
|
|
||||||
val depth = name.length
|
|
||||||
depthCount.getOrPut(depth) { Counter() }.incrementAndGet()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun skip(name: Name, vision: Vision): Boolean = vision is Proxy.ProxyChild
|
|
||||||
|
|
||||||
override suspend fun visitChildren(name: Name, group: VisionGroup) {
|
|
||||||
if (name == "volumes".toName()) return
|
|
||||||
if (group !is MutableVisionGroup) return
|
|
||||||
|
|
||||||
val newChildren = group.children.entries.associate { (visionToken, vision) ->
|
|
||||||
//Reduce single child groups
|
|
||||||
if (vision is VisionGroup && vision !is Proxy && vision.children.size == 1) {
|
|
||||||
val (token, child) = vision.children.entries.first()
|
|
||||||
child.parent = null
|
|
||||||
if (token != visionToken) {
|
|
||||||
child.config["solidName"] = token.toString()
|
|
||||||
}
|
|
||||||
visionToken to child.updateFrom(vision)
|
|
||||||
} else {
|
|
||||||
visionToken to vision
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (newChildren != group.children) {
|
|
||||||
group.removeAll()
|
|
||||||
newChildren.forEach { (token, child) ->
|
|
||||||
group[token] = child
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@DFExperimental
|
internal fun Vision.updateFrom(other: Vision): Vision {
|
||||||
suspend fun SolidGroup.optimizeGdml(): Job = coroutineScope {
|
if (this is Solid && other is Solid) {
|
||||||
prototypes?.let {
|
position = position.safePlus(other.position)
|
||||||
VisionVisitor.visitTree(GdmlOptimizer(), this, it)
|
rotation = rotation.safePlus(other.rotation)
|
||||||
} ?: CompletableDeferred(Unit)
|
if (this.scale != null || other.scale != null) {
|
||||||
|
scaleX = scaleX.toDouble() * other.scaleX.toDouble()
|
||||||
|
scaleY = scaleY.toDouble() * other.scaleY.toDouble()
|
||||||
|
scaleZ = scaleZ.toDouble() * other.scaleZ.toDouble()
|
||||||
|
}
|
||||||
|
other.properties?.sequence()?.forEach { (name, item) ->
|
||||||
|
if (properties?.getItem(name) == null) {
|
||||||
|
config[name] = item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this
|
||||||
}
|
}
|
||||||
|
//
|
||||||
|
//@DFExperimental
|
||||||
|
//private class GdmlOptimizer() : VisionVisitor {
|
||||||
|
// val logger = KotlinLogging.logger("SingleChildReducer")
|
||||||
|
//
|
||||||
|
// private val depthCount = HashMap<Int, Counter>()
|
||||||
|
//
|
||||||
|
// override suspend fun visit(name: Name, vision: Vision) {
|
||||||
|
// val depth = name.length
|
||||||
|
// depthCount.getOrPut(depth) { Counter() }.incrementAndGet()
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// override fun skip(name: Name, vision: Vision): Boolean = vision is Proxy.ProxyChild
|
||||||
|
//
|
||||||
|
// override suspend fun visitChildren(name: Name, group: VisionGroup) {
|
||||||
|
// if (name == "volumes".toName()) return
|
||||||
|
// if (group !is MutableVisionGroup) return
|
||||||
|
//
|
||||||
|
// val newChildren = group.children.entries.associate { (visionToken, vision) ->
|
||||||
|
// //Reduce single child groups
|
||||||
|
// if (vision is VisionGroup && vision !is Proxy && vision.children.size == 1) {
|
||||||
|
// val (token, child) = vision.children.entries.first()
|
||||||
|
// child.parent = null
|
||||||
|
// if (token != visionToken) {
|
||||||
|
// child.config["solidName"] = token.toString()
|
||||||
|
// }
|
||||||
|
// visionToken to child.updateFrom(vision)
|
||||||
|
// } else {
|
||||||
|
// visionToken to vision
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// if (newChildren != group.children) {
|
||||||
|
// group.removeAll()
|
||||||
|
// newChildren.forEach { (token, child) ->
|
||||||
|
// group[token] = child
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//@DFExperimental
|
||||||
|
//suspend fun SolidGroup.optimizeGdml(): Job = coroutineScope {
|
||||||
|
// prototypes?.let {
|
||||||
|
// VisionVisitor.visitTree(GdmlOptimizer(), this, it)
|
||||||
|
// } ?: CompletableDeferred(Unit)
|
||||||
|
//}
|
Loading…
Reference in New Issue
Block a user