From e31ad5ece160b191eddf76be9cd9cd716aa5cfa0 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 28 Jul 2019 22:00:11 +0300 Subject: [PATCH] GDML demo renders something --- .../hep/dataforge/vis/spatial/gdml/units.kt | 76 ++++++++ .../dataforge/vis/spatial/gdml/visualGDML.kt | 179 +++++++++++------- .../vis/spatial/gdml/demo/GDMLDemoApp.kt | 38 +++- .../src/jsMain/web/index.html | 3 + .../src/jsMain/web/main.css | 13 ++ .../dataforge/vis/spatial/three/Materials.kt | 10 +- .../vis/spatial/three/ThreeGeometryBuilder.kt | 5 + .../vis/spatial/three/ThreePlugin.kt | 7 +- .../kotlin/hep/dataforge/vis/spatial/Box.kt | 3 +- .../hep/dataforge/vis/spatial/Composite.kt | 3 +- .../hep/dataforge/vis/spatial/Extruded.kt | 17 +- .../dataforge/vis/spatial/VisualObject3D.kt | 14 -- 12 files changed, 268 insertions(+), 100 deletions(-) create mode 100644 dataforge-vis-spatial-gdml/src/commonMain/kotlin/hep/dataforge/vis/spatial/gdml/units.kt create mode 100644 dataforge-vis-spatial-gdml/src/jsMain/web/main.css diff --git a/dataforge-vis-spatial-gdml/src/commonMain/kotlin/hep/dataforge/vis/spatial/gdml/units.kt b/dataforge-vis-spatial-gdml/src/commonMain/kotlin/hep/dataforge/vis/spatial/gdml/units.kt new file mode 100644 index 00000000..25319f58 --- /dev/null +++ b/dataforge-vis-spatial-gdml/src/commonMain/kotlin/hep/dataforge/vis/spatial/gdml/units.kt @@ -0,0 +1,76 @@ +package hep.dataforge.vis.spatial.gdml + +import scientifik.gdml.GDMLPosition +import scientifik.gdml.GDMLRotation +import scientifik.gdml.GDMLSolid +import kotlin.math.PI + +enum class LUnit(val value: Double) { + MM(1.0), + CM(10.0), + M(1000.0) +} + +enum class AUnit(val value: Double) { + DEG(PI / 180), + RAD(1.0), + RADIAN(1.0) +} + +fun GDMLPosition.unit(): LUnit = LUnit.valueOf(unit.toUpperCase()) + +fun GDMLPosition.x(unit: LUnit): Double = if (unit.name == this.unit) { + x.toDouble() +} else { + x.toDouble() / unit.value * unit().value +} + +fun GDMLPosition.y(unit: LUnit): Double = if (unit.name == this.unit) { + y.toDouble() +} else { + y.toDouble() / unit.value * unit().value +} + +fun GDMLPosition.z(unit: LUnit): Double = if (unit.name == this.unit) { + z.toDouble() +} else { + z.toDouble() / unit.value * unit().value +} + +fun GDMLRotation.unit(): AUnit = AUnit.valueOf(unit.toUpperCase()) + +fun GDMLRotation.x(unit: AUnit = AUnit.RAD): Double = if (unit.name == this.unit) { + x.toDouble() +} else { + x.toDouble() / unit.value * unit().value +} + +fun GDMLRotation.y(unit: AUnit = AUnit.RAD): Double = if (unit.name == this.unit) { + y.toDouble() +} else { + y.toDouble() / unit.value * unit().value +} + +fun GDMLRotation.z(unit: AUnit = AUnit.RAD): Double = if (unit.name == this.unit) { + z.toDouble() +} else { + z.toDouble() / unit.value * unit().value +} + +fun GDMLSolid.lscale(unit: LUnit): Double { + val solidUnit = lunit?.let { LUnit.valueOf(it.toUpperCase()) } ?: return 1.0 + return if (solidUnit == unit) { + 1.0 + } else { + solidUnit.value / unit.value + } +} + +fun GDMLSolid.ascale(unit: AUnit = AUnit.RAD): Double { + val solidUnit = aunit?.let { AUnit.valueOf(it.toUpperCase()) } ?: return 1.0 + return if (solidUnit == unit) { + 1.0 + } else { + solidUnit.value / unit.value + } +} \ No newline at end of file diff --git a/dataforge-vis-spatial-gdml/src/commonMain/kotlin/hep/dataforge/vis/spatial/gdml/visualGDML.kt b/dataforge-vis-spatial-gdml/src/commonMain/kotlin/hep/dataforge/vis/spatial/gdml/visualGDML.kt index 2951dbff..f8f3be26 100644 --- a/dataforge-vis-spatial-gdml/src/commonMain/kotlin/hep/dataforge/vis/spatial/gdml/visualGDML.kt +++ b/dataforge-vis-spatial-gdml/src/commonMain/kotlin/hep/dataforge/vis/spatial/gdml/visualGDML.kt @@ -1,7 +1,7 @@ package hep.dataforge.vis.spatial.gdml -import hep.dataforge.meta.EmptyMeta import hep.dataforge.meta.Meta +import hep.dataforge.meta.buildMeta import hep.dataforge.vis.common.VisualGroup import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.common.color @@ -9,66 +9,64 @@ import hep.dataforge.vis.spatial.* import scientifik.gdml.* import kotlin.math.cos import kotlin.math.sin +import kotlin.random.Random private fun VisualObject.withPosition( + lUnit: LUnit, pos: GDMLPosition? = null, rotation: GDMLRotation? = null, scale: GDMLScale? = null -): VisualObject = - apply { - // if( this is VisualObject3D){ -// pos?.let { -// x = pos.x -// y = pos.y -// z = pos.z -// } -// rotation?.let { -// rotationX = rotation.x -// rotationY = rotation.y -// rotationZ = rotation.z -// } -// } else { - pos?.let { - x = pos.x - y = pos.y - z = pos.z - } - rotation?.let { - rotationX = rotation.x - rotationY = rotation.y - rotationZ = rotation.z - } - //} - scale?.let { - scaleX = scale.x - scaleY = scale.y - scaleZ = scale.z - } - //TODO convert units if needed +): VisualObject = apply { + pos?.let { + x = pos.x(lUnit) + y = pos.y(lUnit) + z = pos.z(lUnit) } + rotation?.let { + rotationX = rotation.x() + rotationY = rotation.y() + rotationZ = rotation.z() + } + scale?.let { + scaleX = scale.x + scaleY = scale.y + scaleZ = scale.z + } + //TODO convert units if needed +} + +private inline operator fun Number.times(d: Double) = toDouble() * d private fun VisualGroup.addSolid( root: GDML, solid: GDMLSolid, + lUnit: LUnit, name: String? = null, block: VisualObject.() -> Unit = {} ): VisualObject { + val lScale = solid.lscale(lUnit) + val aScale = solid.ascale() return when (solid) { - is GDMLBox -> box(solid.x, solid.y, solid.z, name) - is GDMLTube -> cylinder(solid.rmax, solid.z, name) { - startAngle = solid.startphi - angle = solid.deltaphi + is GDMLBox -> box(solid.x * lScale, solid.y * lScale, solid.z * lScale, name) + is GDMLTube -> cylinder(solid.rmax * lScale, solid.z * lScale, name) { + startAngle = solid.startphi * aScale + angle = solid.deltaphi * aScale } is GDMLXtru -> extrude(name) { shape { solid.vertices.forEach { - point(it.x, it.y) + point(it.x * lScale, it.y * lScale) } } solid.sections.sortedBy { it.zOrder }.forEach { section -> - layer(section.zPosition, section.xOffset, section.yOffset, section.scalingFactor) + layer( + section.zPosition * lScale, + section.xOffset * lScale, + section.yOffset * lScale, + section.scalingFactor + ) } } is GDMLScaledSolid -> { @@ -76,31 +74,31 @@ private fun VisualGroup.addSolid( val innerSolid = solid.solidref.resolve(root) ?: error("Solid with tag ${solid.solidref.ref} for scaled solid ${solid.name} not defined") - addSolid(root, innerSolid) { + addSolid(root, innerSolid, lUnit) { block() scaleX = scaleX.toDouble() * solid.scale.x.toDouble() scaleY = scaleY.toDouble() * solid.scale.y.toDouble() scaleZ = scaleZ.toDouble() * solid.scale.z.toDouble() } } - is GDMLSphere -> sphere(solid.rmax, solid.deltaphi, solid.deltatheta, name) { - phiStart = solid.startphi.toDouble() - thetaStart = solid.starttheta.toDouble() + 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, name = name) + 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.toDouble() + val baseRadius = solid.planes.first().rmax * lScale shape { (0..solid.numsides).forEach { - val phi = solid.deltaphi.toDouble() / solid.numsides * it + solid.startphi.toDouble() - baseRadius * cos(phi) to baseRadius * sin(phi) + 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, scale = plane.rmax.toDouble() / baseRadius) + layer(plane.z * lScale, scale = plane.rmax * lScale / baseRadius) } } is GDMLBoolSolid -> { @@ -113,19 +111,59 @@ private fun VisualGroup.addSolid( } return composite(type, name) { - addSolid(root, first) { - withPosition(solid.resolveFirstPosition(root), solid.resolveFirstRotation(root), null) + addSolid(root, first, lUnit) { + withPosition(lUnit, solid.resolveFirstPosition(root), solid.resolveFirstRotation(root), null) + } + addSolid(root, second, lUnit) { + withPosition(lUnit, solid.resolvePosition(root), solid.resolveRotation(root), null) } - addSolid(root, second) - withPosition(solid.resolvePosition(root), solid.resolveRotation(root), null) } } }.apply(block) } +private fun VisualGroup.addPhysicalVolume( + root: GDML, + physVolume: GDMLPhysVolume, + lUnit: LUnit, + resolveColor: GDMLMaterial.() -> Meta +) { + val volume: GDMLGroup = physVolume.volumeref.resolve(root) + ?: error("Volume with ref ${physVolume.volumeref.ref} could not be resolved") + + addVolume( + root, + volume, + lUnit, + physVolume.resolvePosition(root), + physVolume.resolveRotation(root), + physVolume.resolveScale(root), + resolveColor + ) +} + +private fun VisualGroup.addDivisionVolume( + root: GDML, + divisionVolume: GDMLDivisionVolume, + lUnit: LUnit, + resolveColor: GDMLMaterial.() -> Meta +) { + val volume: GDMLGroup = divisionVolume.volumeref.resolve(root) + ?: error("Volume with ref ${divisionVolume.volumeref.ref} could not be resolved") + + //TODO add divisions + addVolume( + root, + volume, + lUnit, + resolveColor = resolveColor + ) +} + private fun VisualGroup.addVolume( root: GDML, group: GDMLGroup, + lUnit: LUnit, position: GDMLPosition? = null, rotation: GDMLRotation? = null, scale: GDMLScale? = null, @@ -133,40 +171,37 @@ private fun VisualGroup.addVolume( ) { group(group.name) { - withPosition(position, rotation, scale) + withPosition(lUnit, position, rotation, scale) if (group is GDMLVolume) { val solid = group.solidref.resolve(root) ?: error("Solid with tag ${group.solidref.ref} for volume ${group.name} not defined") - val material = group.materialref.resolve(root) - ?: error("Material with tag ${group.materialref.ref} for volume ${group.name} not defined") + val material = group.materialref.resolve(root) ?: GDMLElement(group.materialref.ref) + //?: error("Material with tag ${group.materialref.ref} for volume ${group.name} not defined") - addSolid(root, solid, solid.name) { + addSolid(root, solid, lUnit, solid.name) { color(material.resolveColor()) } - //TODO render placements + + when (val vol = group.placement) { + is GDMLPhysVolume -> addPhysicalVolume(root, vol, lUnit, resolveColor) + is GDMLDivisionVolume -> addDivisionVolume(root, vol, lUnit, resolveColor) + } } group.physVolumes.forEach { physVolume -> - val volume: GDMLGroup = physVolume.volumeref.resolve(root) - ?: error("Volume with ref ${physVolume.volumeref.ref} could not be resolved") - - addVolume( - root, - volume, - physVolume.resolvePosition(root), - physVolume.resolveRotation(root), - physVolume.resolveScale(root), - resolveColor - ) + addPhysicalVolume(root, physVolume, lUnit, resolveColor) } } - } +fun GDML.toVisual(lUnit: LUnit = LUnit.MM): VisualGroup { + val cache = HashMap() + val random = Random(111) -fun GDML.toVisual(): VisualGroup { - //TODO add materials cache - fun GDMLMaterial.color(): Meta = EmptyMeta - return VisualGroup().also { it.addVolume(this, world) { color() } } + fun GDMLMaterial.color(): Meta = cache.getOrPut(this) { + buildMeta { "color" to random.nextInt(0, Int.MAX_VALUE) } + } + + return VisualGroup().also { it.addVolume(this, world, lUnit) { color() } } } \ No newline at end of file diff --git a/dataforge-vis-spatial-gdml/src/jsMain/kotlin/hep/dataforge/vis/spatial/gdml/demo/GDMLDemoApp.kt b/dataforge-vis-spatial-gdml/src/jsMain/kotlin/hep/dataforge/vis/spatial/gdml/demo/GDMLDemoApp.kt index 1c13315b..f3ab193a 100644 --- a/dataforge-vis-spatial-gdml/src/jsMain/kotlin/hep/dataforge/vis/spatial/gdml/demo/GDMLDemoApp.kt +++ b/dataforge-vis-spatial-gdml/src/jsMain/kotlin/hep/dataforge/vis/spatial/gdml/demo/GDMLDemoApp.kt @@ -3,9 +3,11 @@ package hep.dataforge.vis.spatial.gdml.demo import hep.dataforge.context.Global import hep.dataforge.vis.hmr.ApplicationBase import hep.dataforge.vis.hmr.startApplication +import hep.dataforge.vis.spatial.gdml.LUnit import hep.dataforge.vis.spatial.gdml.toVisual import hep.dataforge.vis.spatial.three.ThreePlugin import hep.dataforge.vis.spatial.three.output +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import org.w3c.dom.HTMLDivElement @@ -32,7 +34,7 @@ private class GDMLDemoApp : ApplicationBase() { /** * Load data from text file */ - private fun loadData(event: Event, block: suspend (String) -> Unit) { + private fun loadData(event: Event, block: suspend CoroutineScope.(String) -> Unit) { event.stopPropagation() event.preventDefault() @@ -51,6 +53,25 @@ private class GDMLDemoApp : ApplicationBase() { } } + private fun spinner(show: Boolean) { + val style = if (show) { + "display:block;" + } else { + "display:none;" + } + document.getElementById("loader")?.setAttribute("style", style) + } + + private fun message(message: String?) { + val element = document.getElementById("message") + if (message == null) { + element?.setAttribute("style", "display:none;") + } else { + element?.textContent = message + element?.setAttribute("style", "display:block;") + } + } + override fun start(state: Map) { @@ -59,13 +80,22 @@ private class GDMLDemoApp : ApplicationBase() { //val url = URL("https://drive.google.com/open?id=1w5e7fILMN83JGgB8WANJUYm8OW2s0WVO") val canvas = document.getElementById("canvas") ?: error("Element with id canvas not found on page") + canvas.clear() - val action: suspend (String) -> Unit = { + val action: suspend CoroutineScope.(String) -> Unit = { canvas.clear() - val output = three.output(canvas) + launch { spinner(true) } + launch { message("Loading GDML") } val gdml = GDML.format.parse(GDML.serializer(), it) - val visual = gdml.toVisual() + launch { message("Converting GDML into DF-VIS format") } + val visual = gdml.toVisual(LUnit.CM) + launch { message("Rendering") } + val output = three.output(canvas) output.render(visual) + launch { + message(null) + spinner(false) + } } (document.getElementById("drop_zone") as? HTMLDivElement)?.apply { diff --git a/dataforge-vis-spatial-gdml/src/jsMain/web/index.html b/dataforge-vis-spatial-gdml/src/jsMain/web/index.html index bb3faf0a..83164142 100644 --- a/dataforge-vis-spatial-gdml/src/jsMain/web/index.html +++ b/dataforge-vis-spatial-gdml/src/jsMain/web/index.html @@ -6,6 +6,7 @@ Three js demo for particle physics + @@ -18,6 +19,8 @@

GDML demo

+ +