Performance optimization
This commit is contained in:
parent
e31ad5ece1
commit
997a5a8e60
@ -1,11 +1,5 @@
|
||||
package hep.dataforge.vis.common
|
||||
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.MetaBuilder
|
||||
import hep.dataforge.meta.buildMeta
|
||||
import hep.dataforge.meta.set
|
||||
import hep.dataforge.names.toName
|
||||
|
||||
/**
|
||||
* Taken from https://github.com/markaren/three.kt/blob/master/threejs-wrapper/src/main/kotlin/info/laht/threekt/math/ColorConstants.kt
|
||||
*/
|
||||
@ -180,24 +174,4 @@ object Colors {
|
||||
const val whitesmoke = 0xF5F5F5
|
||||
const val yellow = 0xFFFF00
|
||||
const val yellowgreen = 0x9ACD32
|
||||
}
|
||||
|
||||
private val material = "material".toName()
|
||||
|
||||
fun VisualObject.color(rgb: Int) {
|
||||
this.config[material] = rgb
|
||||
}
|
||||
|
||||
fun VisualObject.color(meta: Meta) {
|
||||
this.config[material] = meta
|
||||
}
|
||||
|
||||
fun VisualObject.color(builder: MetaBuilder.() -> Unit) {
|
||||
color(buildMeta(builder))
|
||||
}
|
||||
|
||||
fun VisualObject.color(r: Int, g: Int, b: Int) = color {
|
||||
"red" to r
|
||||
"green" to g
|
||||
"blue" to b
|
||||
}
|
@ -5,22 +5,12 @@ import hep.dataforge.meta.Laminate
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.provider.Provider
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.collections.HashMap
|
||||
import kotlin.collections.HashSet
|
||||
import kotlin.collections.Iterable
|
||||
import kotlin.collections.Iterator
|
||||
import kotlin.collections.Map
|
||||
import kotlin.collections.emptyMap
|
||||
import kotlin.collections.forEach
|
||||
import kotlin.collections.plus
|
||||
import kotlin.collections.removeAll
|
||||
import kotlin.collections.set
|
||||
|
||||
/**
|
||||
* A display group which allows both named and unnamed children
|
||||
*/
|
||||
class VisualGroup(
|
||||
open class VisualGroup(
|
||||
override val parent: VisualObject? = null, tagRefs: Array<out Meta> = emptyArray()
|
||||
) : VisualObject, Iterable<VisualObject>, Provider {
|
||||
|
||||
@ -30,7 +20,9 @@ class VisualGroup(
|
||||
override val defaultTarget: String get() = VisualObject.TYPE
|
||||
override val config = Config()
|
||||
|
||||
override val properties: Laminate by lazy { combineProperties(parent, config, tagRefs) }
|
||||
override val properties: Laminate by lazy {
|
||||
combineProperties(parent, config, tagRefs)
|
||||
}
|
||||
|
||||
override fun iterator(): Iterator<VisualObject> = (namedChildren.values + unnamedChildren).iterator()
|
||||
|
||||
|
@ -78,9 +78,11 @@ open class VisualLeaf(
|
||||
final override val parent: VisualObject?,
|
||||
tagRefs: Array<out Meta>
|
||||
) : VisualObject {
|
||||
final override val config = Config()
|
||||
override val config = Config()
|
||||
|
||||
override val properties: Laminate by lazy { combineProperties(parent, config, tagRefs) }
|
||||
override val properties: Laminate by lazy {
|
||||
combineProperties(parent, config, tagRefs)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun combineProperties(parent: VisualObject?, config: Config, tagRefs: Array<out Meta>): Laminate {
|
||||
|
@ -5,71 +5,72 @@ 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 LUnit(val value: Float) {
|
||||
MM(1f),
|
||||
CM(10f),
|
||||
M(1000f)
|
||||
}
|
||||
|
||||
enum class AUnit(val value: Double) {
|
||||
DEG(PI / 180),
|
||||
RAD(1.0),
|
||||
RADIAN(1.0)
|
||||
enum class AUnit(val value: Float) {
|
||||
DEG(PI.toFloat() / 180),
|
||||
DEGREE(PI.toFloat() / 180),
|
||||
RAD(1f),
|
||||
RADIAN(1f)
|
||||
}
|
||||
|
||||
fun GDMLPosition.unit(): LUnit = LUnit.valueOf(unit.toUpperCase())
|
||||
|
||||
fun GDMLPosition.x(unit: LUnit): Double = if (unit.name == this.unit) {
|
||||
x.toDouble()
|
||||
fun GDMLPosition.x(unit: LUnit): Float = if (unit.name == this.unit) {
|
||||
x.toFloat()
|
||||
} else {
|
||||
x.toDouble() / unit.value * unit().value
|
||||
x.toFloat() / unit.value * unit().value
|
||||
}
|
||||
|
||||
fun GDMLPosition.y(unit: LUnit): Double = if (unit.name == this.unit) {
|
||||
y.toDouble()
|
||||
fun GDMLPosition.y(unit: LUnit): Float = if (unit.name == this.unit) {
|
||||
y.toFloat()
|
||||
} else {
|
||||
y.toDouble() / unit.value * unit().value
|
||||
y.toFloat() / unit.value * unit().value
|
||||
}
|
||||
|
||||
fun GDMLPosition.z(unit: LUnit): Double = if (unit.name == this.unit) {
|
||||
z.toDouble()
|
||||
fun GDMLPosition.z(unit: LUnit): Float = if (unit.name == this.unit) {
|
||||
z.toFloat()
|
||||
} else {
|
||||
z.toDouble() / unit.value * unit().value
|
||||
z.toFloat() / 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()
|
||||
fun GDMLRotation.x(unit: AUnit = AUnit.RAD): Float = if (unit.name == this.unit) {
|
||||
x.toFloat()
|
||||
} else {
|
||||
x.toDouble() / unit.value * unit().value
|
||||
x.toFloat() / unit.value * unit().value
|
||||
}
|
||||
|
||||
fun GDMLRotation.y(unit: AUnit = AUnit.RAD): Double = if (unit.name == this.unit) {
|
||||
y.toDouble()
|
||||
fun GDMLRotation.y(unit: AUnit = AUnit.RAD): Float = if (unit.name == this.unit) {
|
||||
y.toFloat()
|
||||
} else {
|
||||
y.toDouble() / unit.value * unit().value
|
||||
y.toFloat() / unit.value * unit().value
|
||||
}
|
||||
|
||||
fun GDMLRotation.z(unit: AUnit = AUnit.RAD): Double = if (unit.name == this.unit) {
|
||||
z.toDouble()
|
||||
fun GDMLRotation.z(unit: AUnit = AUnit.RAD): Float = if (unit.name == this.unit) {
|
||||
z.toFloat()
|
||||
} else {
|
||||
z.toDouble() / unit.value * unit().value
|
||||
z.toFloat() / unit.value * unit().value
|
||||
}
|
||||
|
||||
fun GDMLSolid.lscale(unit: LUnit): Double {
|
||||
val solidUnit = lunit?.let { LUnit.valueOf(it.toUpperCase()) } ?: return 1.0
|
||||
fun GDMLSolid.lscale(unit: LUnit): Float {
|
||||
val solidUnit = lunit?.let { LUnit.valueOf(it.toUpperCase()) } ?: return 1f
|
||||
return if (solidUnit == unit) {
|
||||
1.0
|
||||
1f
|
||||
} else {
|
||||
solidUnit.value / unit.value
|
||||
}
|
||||
}
|
||||
|
||||
fun GDMLSolid.ascale(unit: AUnit = AUnit.RAD): Double {
|
||||
val solidUnit = aunit?.let { AUnit.valueOf(it.toUpperCase()) } ?: return 1.0
|
||||
fun GDMLSolid.ascale(unit: AUnit = AUnit.RAD): Float {
|
||||
val solidUnit = aunit?.let { AUnit.valueOf(it.toUpperCase()) } ?: return 1f
|
||||
return if (solidUnit == unit) {
|
||||
1.0
|
||||
1f
|
||||
} else {
|
||||
solidUnit.value / unit.value
|
||||
}
|
||||
|
@ -2,9 +2,7 @@ package hep.dataforge.vis.spatial.gdml
|
||||
|
||||
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
|
||||
import hep.dataforge.meta.builder
|
||||
import hep.dataforge.vis.spatial.*
|
||||
import scientifik.gdml.*
|
||||
import kotlin.math.cos
|
||||
@ -12,41 +10,87 @@ import kotlin.math.sin
|
||||
import kotlin.random.Random
|
||||
|
||||
|
||||
private fun VisualObject.withPosition(
|
||||
class GDMLTransformer(val root: GDML) {
|
||||
private val cache = HashMap<GDMLMaterial, Meta>()
|
||||
private val random = Random(111)
|
||||
|
||||
var lUnit: LUnit = LUnit.MM
|
||||
var resolveColor: ColorResolver = { material, _ ->
|
||||
val materialColor = cache.getOrPut(material) {
|
||||
buildMeta {
|
||||
"color" to random.nextInt(0, Int.MAX_VALUE)
|
||||
}
|
||||
}
|
||||
|
||||
if (this?.physVolumes?.isEmpty() != false) {
|
||||
materialColor
|
||||
} else {
|
||||
materialColor.builder().apply { "opacity" to 0.5 }
|
||||
}
|
||||
}
|
||||
|
||||
var acceptSolid: (GDMLSolid) -> Boolean = { true }
|
||||
var acceptGroup: (GDMLGroup) -> Boolean = { true }
|
||||
|
||||
fun printStatistics() {
|
||||
println("Solids:")
|
||||
solidCounter.entries.sortedByDescending { it.value }.forEach {
|
||||
println("\t$it")
|
||||
}
|
||||
println(println("Solids total: ${solidCounter.values.sum()}"))
|
||||
}
|
||||
|
||||
private val solidCounter = HashMap<String, Int>()
|
||||
|
||||
internal fun solidAdded(solid: GDMLSolid) {
|
||||
solidCounter[solid.name] = (solidCounter[solid.name] ?: 0) + 1
|
||||
}
|
||||
|
||||
var onFinish: GDMLTransformer.() -> Unit = {}
|
||||
|
||||
internal fun finished(){
|
||||
onFinish(this)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private fun VisualObject3D.withPosition(
|
||||
lUnit: LUnit,
|
||||
pos: GDMLPosition? = null,
|
||||
rotation: GDMLRotation? = null,
|
||||
scale: GDMLScale? = null
|
||||
): VisualObject = apply {
|
||||
): VisualObject3D = apply {
|
||||
pos?.let {
|
||||
x = pos.x(lUnit)
|
||||
y = pos.y(lUnit)
|
||||
z = pos.z(lUnit)
|
||||
this@withPosition.position.x = pos.x(lUnit)
|
||||
this@withPosition.position.y = pos.y(lUnit)
|
||||
this@withPosition.position.z = pos.z(lUnit)
|
||||
}
|
||||
rotation?.let {
|
||||
rotationX = rotation.x()
|
||||
rotationY = rotation.y()
|
||||
rotationZ = rotation.z()
|
||||
this@withPosition.rotation.x = rotation.x()
|
||||
this@withPosition.rotation.y = rotation.y()
|
||||
this@withPosition.rotation.z = rotation.z()
|
||||
}
|
||||
scale?.let {
|
||||
scaleX = scale.x
|
||||
scaleY = scale.y
|
||||
scaleZ = scale.z
|
||||
this@withPosition.scale.x = scale.x.toFloat()
|
||||
this@withPosition.scale.y = scale.y.toFloat()
|
||||
this@withPosition.scale.z = scale.z.toFloat()
|
||||
}
|
||||
//TODO convert units if needed
|
||||
}
|
||||
|
||||
private inline operator fun Number.times(d: Double) = toDouble() * d
|
||||
private inline operator fun Number.times(f: Float) = toFloat() * f
|
||||
|
||||
|
||||
private fun VisualGroup.addSolid(
|
||||
root: GDML,
|
||||
private fun VisualGroup3D.addSolid(
|
||||
context: GDMLTransformer,
|
||||
solid: GDMLSolid,
|
||||
lUnit: LUnit,
|
||||
name: String? = null,
|
||||
block: VisualObject.() -> Unit = {}
|
||||
): VisualObject {
|
||||
val lScale = solid.lscale(lUnit)
|
||||
block: VisualObject3D.() -> Unit = {}
|
||||
): VisualObject3D {
|
||||
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)
|
||||
@ -71,14 +115,14 @@ private fun VisualGroup.addSolid(
|
||||
}
|
||||
is GDMLScaledSolid -> {
|
||||
//Add solid with modified scale
|
||||
val innerSolid = solid.solidref.resolve(root)
|
||||
val innerSolid = solid.solidref.resolve(context.root)
|
||||
?: error("Solid with tag ${solid.solidref.ref} for scaled solid ${solid.name} not defined")
|
||||
|
||||
addSolid(root, innerSolid, lUnit) {
|
||||
addSolid(context, innerSolid) {
|
||||
block()
|
||||
scaleX = scaleX.toDouble() * solid.scale.x.toDouble()
|
||||
scaleY = scaleY.toDouble() * solid.scale.y.toDouble()
|
||||
scaleZ = scaleZ.toDouble() * solid.scale.z.toDouble()
|
||||
scale.x *= solid.scale.x.toFloat()
|
||||
scale.y *= solid.scale.y.toFloat()
|
||||
scale.z = solid.scale.z.toFloat()
|
||||
}
|
||||
}
|
||||
is GDMLSphere -> sphere(solid.rmax * lScale, solid.deltaphi * aScale, solid.deltatheta * aScale, name) {
|
||||
@ -102,8 +146,8 @@ private fun VisualGroup.addSolid(
|
||||
}
|
||||
}
|
||||
is GDMLBoolSolid -> {
|
||||
val first = solid.first.resolve(root) ?: error("")
|
||||
val second = solid.second.resolve(root) ?: error("")
|
||||
val first = solid.first.resolve(context.root) ?: error("")
|
||||
val second = solid.second.resolve(context.root) ?: error("")
|
||||
val type: CompositeType = when (solid) {
|
||||
is GDMLUnion -> CompositeType.UNION
|
||||
is GDMLSubtraction -> CompositeType.SUBTRACT
|
||||
@ -111,97 +155,114 @@ private fun VisualGroup.addSolid(
|
||||
}
|
||||
|
||||
return composite(type, name) {
|
||||
addSolid(root, first, lUnit) {
|
||||
withPosition(lUnit, solid.resolveFirstPosition(root), solid.resolveFirstRotation(root), null)
|
||||
addSolid(context, first) {
|
||||
withPosition(
|
||||
context.lUnit,
|
||||
solid.resolveFirstPosition(context.root),
|
||||
solid.resolveFirstRotation(context.root),
|
||||
null
|
||||
)
|
||||
}
|
||||
addSolid(root, second, lUnit) {
|
||||
withPosition(lUnit, solid.resolvePosition(root), solid.resolveRotation(root), null)
|
||||
addSolid(context, second) {
|
||||
withPosition(
|
||||
context.lUnit,
|
||||
solid.resolvePosition(context.root),
|
||||
solid.resolveRotation(context.root),
|
||||
null
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}.apply(block)
|
||||
}
|
||||
|
||||
private fun VisualGroup.addPhysicalVolume(
|
||||
root: GDML,
|
||||
physVolume: GDMLPhysVolume,
|
||||
lUnit: LUnit,
|
||||
resolveColor: GDMLMaterial.() -> Meta
|
||||
private fun VisualGroup3D.addPhysicalVolume(
|
||||
context: GDMLTransformer,
|
||||
physVolume: GDMLPhysVolume
|
||||
) {
|
||||
val volume: GDMLGroup = physVolume.volumeref.resolve(root)
|
||||
val volume: GDMLGroup = physVolume.volumeref.resolve(context.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
|
||||
)
|
||||
if (context.acceptGroup(volume)) {
|
||||
|
||||
this[physVolume.name] = volume(
|
||||
context,
|
||||
volume,
|
||||
physVolume.resolvePosition(context.root),
|
||||
physVolume.resolveRotation(context.root),
|
||||
physVolume.resolveScale(context.root)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun VisualGroup.addDivisionVolume(
|
||||
root: GDML,
|
||||
divisionVolume: GDMLDivisionVolume,
|
||||
lUnit: LUnit,
|
||||
resolveColor: GDMLMaterial.() -> Meta
|
||||
private fun VisualGroup3D.addDivisionVolume(
|
||||
context: GDMLTransformer,
|
||||
divisionVolume: GDMLDivisionVolume
|
||||
) {
|
||||
val volume: GDMLGroup = divisionVolume.volumeref.resolve(root)
|
||||
val volume: GDMLGroup = divisionVolume.volumeref.resolve(context.root)
|
||||
?: error("Volume with ref ${divisionVolume.volumeref.ref} could not be resolved")
|
||||
|
||||
//TODO add divisions
|
||||
addVolume(
|
||||
root,
|
||||
volume,
|
||||
lUnit,
|
||||
resolveColor = resolveColor
|
||||
add(
|
||||
volume(
|
||||
context,
|
||||
volume
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun VisualGroup.addVolume(
|
||||
root: GDML,
|
||||
private fun VisualGroup3D.addVolume(
|
||||
context: GDMLTransformer,
|
||||
group: GDMLGroup,
|
||||
lUnit: LUnit,
|
||||
position: GDMLPosition? = null,
|
||||
rotation: GDMLRotation? = null,
|
||||
scale: GDMLScale? = null,
|
||||
resolveColor: GDMLMaterial.() -> Meta
|
||||
scale: GDMLScale? = null
|
||||
) {
|
||||
this[group.name] = volume(context, group, position, rotation, scale)
|
||||
}
|
||||
|
||||
group(group.name) {
|
||||
withPosition(lUnit, position, rotation, scale)
|
||||
private fun volume(
|
||||
context: GDMLTransformer,
|
||||
group: GDMLGroup,
|
||||
position: GDMLPosition? = null,
|
||||
rotation: GDMLRotation? = null,
|
||||
scale: GDMLScale? = null
|
||||
): VisualGroup3D {
|
||||
|
||||
return VisualGroup3D().apply {
|
||||
withPosition(context.lUnit, position, rotation, scale)
|
||||
|
||||
if (group is GDMLVolume) {
|
||||
val solid = group.solidref.resolve(root)
|
||||
val solid = group.solidref.resolve(context.root)
|
||||
?: error("Solid with tag ${group.solidref.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")
|
||||
val material = group.materialref.resolve(context.root) ?: GDMLElement(group.materialref.ref)
|
||||
|
||||
addSolid(root, solid, lUnit, solid.name) {
|
||||
color(material.resolveColor())
|
||||
if (context.acceptSolid(solid)) {
|
||||
addSolid(context, solid, solid.name) {
|
||||
this.material = context.resolveColor(group, material, solid)
|
||||
}
|
||||
}
|
||||
|
||||
when (val vol = group.placement) {
|
||||
is GDMLPhysVolume -> addPhysicalVolume(root, vol, lUnit, resolveColor)
|
||||
is GDMLDivisionVolume -> addDivisionVolume(root, vol, lUnit, resolveColor)
|
||||
is GDMLPhysVolume -> addPhysicalVolume(context, vol)
|
||||
is GDMLDivisionVolume -> addDivisionVolume(context, vol)
|
||||
}
|
||||
}
|
||||
|
||||
group.physVolumes.forEach { physVolume ->
|
||||
addPhysicalVolume(root, physVolume, lUnit, resolveColor)
|
||||
addPhysicalVolume(context, physVolume)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun GDML.toVisual(lUnit: LUnit = LUnit.MM): VisualGroup {
|
||||
val cache = HashMap<GDMLMaterial, Meta>()
|
||||
val random = Random(111)
|
||||
typealias ColorResolver = GDMLGroup?.(GDMLMaterial, GDMLSolid?) -> Meta
|
||||
|
||||
fun GDMLMaterial.color(): Meta = cache.getOrPut(this) {
|
||||
buildMeta { "color" to random.nextInt(0, Int.MAX_VALUE) }
|
||||
|
||||
fun GDML.toVisual(block: GDMLTransformer.() -> Unit = {}): VisualGroup3D {
|
||||
|
||||
val context = GDMLTransformer(this).apply(block)
|
||||
|
||||
return volume(context, world).also{
|
||||
context.finished()
|
||||
}
|
||||
|
||||
return VisualGroup().also { it.addVolume(this, world, lUnit) { color() } }
|
||||
}
|
@ -88,7 +88,14 @@ private class GDMLDemoApp : ApplicationBase() {
|
||||
launch { message("Loading GDML") }
|
||||
val gdml = GDML.format.parse(GDML.serializer(), it)
|
||||
launch { message("Converting GDML into DF-VIS format") }
|
||||
val visual = gdml.toVisual(LUnit.CM)
|
||||
val visual = gdml.toVisual {
|
||||
lUnit = LUnit.CM
|
||||
acceptSolid = { solid ->
|
||||
!solid.name.startsWith("ecal")
|
||||
&& !solid.name.startsWith("V")
|
||||
&& !solid.name.startsWith("U")
|
||||
}
|
||||
}
|
||||
launch { message("Rendering") }
|
||||
val output = three.output(canvas)
|
||||
output.render(visual)
|
||||
|
@ -0,0 +1,25 @@
|
||||
package hep.dataforge.vis.spatial.gdml
|
||||
|
||||
import nl.adaptivity.xmlutil.StAXReader
|
||||
import scientifik.gdml.GDML
|
||||
import java.io.File
|
||||
import java.net.URL
|
||||
|
||||
fun main() {
|
||||
val url = URL("https://drive.google.com/open?id=1w5e7fILMN83JGgB8WANJUYm8OW2s0WVO")
|
||||
val file = File("D:\\Work\\Projects\\gdml.kt\\gdml-source\\BM@N.gdml")
|
||||
val stream = if (file.exists()) {
|
||||
file.inputStream()
|
||||
} else {
|
||||
url.openStream()
|
||||
}
|
||||
|
||||
val xmlReader = StAXReader(stream, "UTF-8")
|
||||
val xml = GDML.format.parse(GDML.serializer(), xmlReader)
|
||||
xml.toVisual {
|
||||
lUnit = LUnit.CM
|
||||
acceptSolid = { solid -> !solid.name.startsWith("ecal") && !solid.name.startsWith("V") }
|
||||
onFinish = { printStatistics() }
|
||||
}
|
||||
readLine()
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
package hep.dataforge.vis.spatial.gdml
|
||||
|
||||
import nl.adaptivity.xmlutil.StAXReader
|
||||
import org.junit.Test
|
||||
import scientifik.gdml.GDML
|
||||
import java.io.File
|
||||
import java.net.URL
|
||||
|
||||
class BMNTest {
|
||||
|
||||
@Test
|
||||
fun testRead() {
|
||||
|
||||
val url = URL("https://drive.google.com/open?id=1w5e7fILMN83JGgB8WANJUYm8OW2s0WVO")
|
||||
val file = File("D:\\Work\\Projects\\gdml.kt\\src\\commonTest\\resources\\gdml\\geofile_full.xml")
|
||||
val stream = if (file.exists()) {
|
||||
file.inputStream()
|
||||
} else {
|
||||
url.openStream()
|
||||
}
|
||||
|
||||
val xmlReader = StAXReader(stream, "UTF-8")
|
||||
val xml = GDML.format.parse(GDML.serializer(), xmlReader)
|
||||
repeat(20) {
|
||||
xml.toVisual()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package hep.dataforge.vis.spatial.gdml
|
||||
|
||||
import nl.adaptivity.xmlutil.StAXReader
|
||||
import org.junit.Test
|
||||
import scientifik.gdml.GDML
|
||||
import java.io.File
|
||||
import java.net.URL
|
||||
|
||||
class TestConvertor {
|
||||
|
||||
@Test
|
||||
fun testBMNGeometry() {
|
||||
val url = URL("https://drive.google.com/open?id=1w5e7fILMN83JGgB8WANJUYm8OW2s0WVO")
|
||||
val file = File("D:\\Work\\Projects\\gdml.kt\\gdml-source\\BM@N.gdml")
|
||||
val stream = if (file.exists()) {
|
||||
file.inputStream()
|
||||
} else {
|
||||
url.openStream()
|
||||
}
|
||||
|
||||
val xmlReader = StAXReader(stream, "UTF-8")
|
||||
val xml = GDML.format.parse(GDML.serializer(), xmlReader)
|
||||
xml.toVisual()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCubes() {
|
||||
val file = File("D:\\Work\\Projects\\gdml.kt\\gdml-source\\cubes.gdml ")
|
||||
val stream = if (file.exists()) {
|
||||
file.inputStream()
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
val xmlReader = StAXReader(stream, "UTF-8")
|
||||
val xml = GDML.format.parse(GDML.serializer(), xmlReader)
|
||||
val visual = xml.toVisual()
|
||||
println(visual)
|
||||
}
|
||||
}
|
@ -1,9 +1,7 @@
|
||||
package hep.dataforge.vis.spatial.demo
|
||||
|
||||
import hep.dataforge.context.ContextBuilder
|
||||
import hep.dataforge.meta.number
|
||||
import hep.dataforge.vis.common.Colors
|
||||
import hep.dataforge.vis.common.color
|
||||
import hep.dataforge.vis.hmr.ApplicationBase
|
||||
import hep.dataforge.vis.hmr.startApplication
|
||||
import hep.dataforge.vis.spatial.*
|
||||
@ -42,6 +40,7 @@ private class ThreeDemoApp : ApplicationBase() {
|
||||
box(100, 100, 100) {
|
||||
z = 110.0
|
||||
}
|
||||
|
||||
box(100, 100, 100) {
|
||||
visible = false
|
||||
x = 110.0
|
||||
@ -51,32 +50,32 @@ private class ThreeDemoApp : ApplicationBase() {
|
||||
GlobalScope.launch {
|
||||
while (isActive) {
|
||||
delay(500)
|
||||
visible = !visible
|
||||
visible = !(visible ?: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var material by group.config.number(1530).int
|
||||
|
||||
GlobalScope.launch {
|
||||
val random = Random(111)
|
||||
while (isActive) {
|
||||
delay(1000)
|
||||
material = random.nextInt(0, Int.MAX_VALUE)
|
||||
group.color(random.nextInt(0, Int.MAX_VALUE))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// demo("jsroot", "JSROOT cube") {
|
||||
// jsRootGeometry {
|
||||
// y = 110.0
|
||||
// shape = box(50, 50, 50)
|
||||
// color(Colors.lightcoral)
|
||||
// rotationX = PI / 4
|
||||
// rotationY = PI / 4
|
||||
// }
|
||||
// }
|
||||
demo("rotation", "Rotations") {
|
||||
box(100, 100, 100)
|
||||
group {
|
||||
x = 200
|
||||
rotationY = PI / 4
|
||||
box(100, 100, 100) {
|
||||
rotationZ = PI / 4
|
||||
color(Colors.red)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
demo("extrude", "extruded shape") {
|
||||
extrude {
|
||||
@ -86,9 +85,8 @@ private class ThreeDemoApp : ApplicationBase() {
|
||||
for (i in 0..100) {
|
||||
layer(i * 5, 20 * sin(2 * PI / 100 * i), 20 * cos(2 * PI / 100 * i))
|
||||
}
|
||||
color(Colors.teal)
|
||||
}
|
||||
|
||||
color(Colors.teal)
|
||||
}
|
||||
|
||||
demo("CSG", "CSG operations") {
|
||||
@ -97,7 +95,7 @@ private class ThreeDemoApp : ApplicationBase() {
|
||||
z = 50
|
||||
}
|
||||
sphere(50)
|
||||
color {
|
||||
material {
|
||||
"color" to Colors.lightgreen
|
||||
"opacity" to 0.3
|
||||
}
|
||||
|
@ -12,8 +12,8 @@ import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.toName
|
||||
import hep.dataforge.output.Output
|
||||
import hep.dataforge.output.OutputManager
|
||||
import hep.dataforge.vis.common.VisualGroup
|
||||
import hep.dataforge.vis.common.VisualObject
|
||||
import hep.dataforge.vis.spatial.VisualGroup3D
|
||||
import hep.dataforge.vis.spatial.render
|
||||
import hep.dataforge.vis.spatial.three.ThreeOutput
|
||||
import hep.dataforge.vis.spatial.three.ThreePlugin
|
||||
@ -79,7 +79,7 @@ class ThreeDemoGrid(meta: Meta) : AbstractPlugin(meta), OutputManager {
|
||||
}
|
||||
}
|
||||
|
||||
fun ThreeDemoGrid.demo(name: String, title: String = name, block: VisualGroup.() -> Unit) {
|
||||
fun ThreeDemoGrid.demo(name: String, title: String = name, block: VisualGroup3D.() -> Unit) {
|
||||
val meta = buildMeta {
|
||||
"title" to title
|
||||
}
|
||||
|
@ -10,8 +10,9 @@ import info.laht.threekt.math.Color
|
||||
|
||||
|
||||
object Materials {
|
||||
val DEFAULT_COLOR = Color(Colors.darkgreen)
|
||||
val DEFAULT = MeshPhongMaterial().apply {
|
||||
this.color.set(Colors.darkgreen)
|
||||
this.color.set(DEFAULT_COLOR)
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,19 +43,18 @@ fun MetaItem<*>.color(): Color {
|
||||
/**
|
||||
* Infer Three material based on meta item
|
||||
*/
|
||||
fun MetaItem<*>?.material(): Material {
|
||||
return when (this) {
|
||||
null -> Materials.DEFAULT
|
||||
is MetaItem.ValueItem -> MeshBasicMaterial().apply {
|
||||
color = this@material.color()
|
||||
}
|
||||
is MetaItem.NodeItem -> MeshBasicMaterial().apply {
|
||||
(node["color"] ?: this@material).let { color = it.color() }
|
||||
opacity = node["opacity"]?.double ?: 1.0
|
||||
transparent = node["transparent"].boolean ?: (opacity < 1.0)
|
||||
fun Meta?.jsMaterial(): Material {
|
||||
return if(this == null){
|
||||
Materials.DEFAULT
|
||||
} else
|
||||
//TODO add more oprions for material
|
||||
MeshBasicMaterial().apply {
|
||||
color = get("color")?.color()?: Materials.DEFAULT_COLOR
|
||||
opacity = get("opacity")?.double ?: 1.0
|
||||
transparent = get("transparent").boolean ?: (opacity < 1.0)
|
||||
//node["specularColor"]?.let { specular = it.color() }
|
||||
side = 2
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -4,11 +4,12 @@ import hep.dataforge.vis.spatial.Cylinder
|
||||
import hep.dataforge.vis.spatial.detail
|
||||
import info.laht.threekt.core.BufferGeometry
|
||||
import info.laht.threekt.geometries.CylinderBufferGeometry
|
||||
import kotlin.math.PI
|
||||
import kotlin.math.pow
|
||||
|
||||
object ThreeCylinderFactory : MeshThreeFactory<Cylinder>(Cylinder::class) {
|
||||
override fun buildGeometry(obj: Cylinder): BufferGeometry {
|
||||
return obj.detail?.let {
|
||||
val cylinder = obj.detail?.let {
|
||||
val segments = it.toDouble().pow(0.5).toInt()
|
||||
CylinderBufferGeometry(
|
||||
radiusTop = obj.upperRadius,
|
||||
@ -28,5 +29,6 @@ object ThreeCylinderFactory : MeshThreeFactory<Cylinder>(Cylinder::class) {
|
||||
thetaStart = obj.startAngle,
|
||||
thetaLength = obj.angle
|
||||
)
|
||||
return cylinder.rotateX(PI/2)
|
||||
}
|
||||
}
|
@ -2,9 +2,10 @@ package hep.dataforge.vis.spatial.three
|
||||
|
||||
import hep.dataforge.meta.boolean
|
||||
import hep.dataforge.meta.get
|
||||
import hep.dataforge.meta.int
|
||||
import hep.dataforge.meta.node
|
||||
import hep.dataforge.names.startsWith
|
||||
import hep.dataforge.provider.Type
|
||||
import hep.dataforge.vis.common.VisualObject
|
||||
import hep.dataforge.vis.common.onChange
|
||||
import hep.dataforge.vis.spatial.*
|
||||
import hep.dataforge.vis.spatial.three.ThreeFactory.Companion.TYPE
|
||||
@ -18,13 +19,11 @@ import info.laht.threekt.objects.LineSegments
|
||||
import info.laht.threekt.objects.Mesh
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
internal val VisualObject.material get() = properties["material"].material()
|
||||
|
||||
/**
|
||||
* Builder and updater for three.js object
|
||||
*/
|
||||
@Type(TYPE)
|
||||
interface ThreeFactory<T : VisualObject> {
|
||||
interface ThreeFactory<T : VisualObject3D> {
|
||||
|
||||
val type: KClass<out T>
|
||||
|
||||
@ -33,46 +32,50 @@ interface ThreeFactory<T : VisualObject> {
|
||||
companion object {
|
||||
const val TYPE = "threeFactory"
|
||||
|
||||
fun <T : VisualObject> buildMesh(obj: T, geometryBuilder: (T) -> BufferGeometry): Mesh {
|
||||
fun <T : VisualObject3D> buildMesh(obj: T, geometryBuilder: (T) -> BufferGeometry): Mesh {
|
||||
val geometry = geometryBuilder(obj)
|
||||
|
||||
//JS sometimes tries to pass Geometry as BufferGeometry
|
||||
@Suppress("USELESS_IS_CHECK") if (geometry !is BufferGeometry) error("BufferGeometry expected")
|
||||
|
||||
val mesh = Mesh(geometry, obj.material)
|
||||
val mesh = Mesh(geometry, obj.material.jsMaterial())
|
||||
|
||||
//inherited edges definition, enabled by default
|
||||
if (obj.properties["edges.enabled"].boolean != false) {
|
||||
val material = obj.properties["edges.material"]?.material() ?: Materials.DEFAULT
|
||||
val material = obj.properties["edges.material"].node?.jsMaterial() ?: Materials.DEFAULT
|
||||
mesh.add(LineSegments(EdgesGeometry(mesh.geometry as BufferGeometry), material))
|
||||
}
|
||||
|
||||
//inherited wireframe definition, disabled by default
|
||||
if (obj.properties["wireframe.enabled"].boolean == true) {
|
||||
val material = obj.properties["edges.material"]?.material() ?: Materials.DEFAULT
|
||||
val material = obj.properties["wireframe.material"].node?.jsMaterial() ?: Materials.DEFAULT
|
||||
mesh.add(LineSegments(WireframeGeometry(mesh.geometry as BufferGeometry), material))
|
||||
}
|
||||
|
||||
//set position for meseh
|
||||
//set position for mesh
|
||||
mesh.updatePosition(obj)
|
||||
|
||||
obj.config["layer"].int?.let {
|
||||
mesh.layers.set(it)
|
||||
}
|
||||
|
||||
//add listener to object properties
|
||||
obj.onChange(this) { name, _, _ ->
|
||||
if (name.toString() == "material") {
|
||||
if (name.startsWith(VisualObject3D.materialKey)) {
|
||||
//updated material
|
||||
mesh.material = obj.material
|
||||
mesh.material = obj.material.jsMaterial()
|
||||
} else if (
|
||||
name.startsWith(PropertyNames3D.position) ||
|
||||
name.startsWith(PropertyNames3D.rotation) ||
|
||||
name.startsWith(PropertyNames3D.scale) ||
|
||||
name.toString() == "visible"
|
||||
name.startsWith(VisualObject3D.position) ||
|
||||
name.startsWith(VisualObject3D.rotation) ||
|
||||
name.startsWith(VisualObject3D.scale) ||
|
||||
name == VisualObject3D.visibleKey
|
||||
) {
|
||||
//update position of mesh using this object
|
||||
mesh.updatePosition(obj)
|
||||
} else {
|
||||
//full update
|
||||
mesh.geometry = geometryBuilder(obj)
|
||||
mesh.material = obj.material
|
||||
mesh.material = obj.material.jsMaterial()
|
||||
}
|
||||
}
|
||||
return mesh
|
||||
@ -83,17 +86,17 @@ interface ThreeFactory<T : VisualObject> {
|
||||
/**
|
||||
* Update position, rotation and visibility
|
||||
*/
|
||||
internal fun Object3D.updatePosition(obj: VisualObject) {
|
||||
internal fun Object3D.updatePosition(obj: VisualObject3D) {
|
||||
position.set(obj.x, obj.y, obj.z)
|
||||
setRotationFromEuler(obj.euler)
|
||||
scale.set(obj.scaleX, obj.scaleY, obj.scaleZ)
|
||||
visible = obj.visible
|
||||
visible = obj.visible ?: true
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsafe invocation of a factory
|
||||
*/
|
||||
operator fun <T : VisualObject> ThreeFactory<T>.invoke(obj: Any): Object3D {
|
||||
operator fun <T : VisualObject3D> ThreeFactory<T>.invoke(obj: Any): Object3D {
|
||||
if (type.isInstance(obj)) {
|
||||
return invoke(obj as T)
|
||||
} else {
|
||||
@ -104,8 +107,7 @@ operator fun <T : VisualObject> ThreeFactory<T>.invoke(obj: Any): Object3D {
|
||||
/**
|
||||
* Basic geometry-based factory
|
||||
*/
|
||||
abstract class MeshThreeFactory<T : VisualObject>(override val type: KClass<out T>) :
|
||||
ThreeFactory<T> {
|
||||
abstract class MeshThreeFactory<T : VisualObject3D>(override val type: KClass<out T>) : ThreeFactory<T> {
|
||||
/**
|
||||
* Build a geometry for an object
|
||||
*/
|
||||
@ -114,8 +116,7 @@ abstract class MeshThreeFactory<T : VisualObject>(override val type: KClass<out
|
||||
|
||||
override fun invoke(obj: T): Mesh {
|
||||
//create mesh from geometry
|
||||
val mesh = buildMesh(obj, ::buildGeometry)
|
||||
return mesh
|
||||
return buildMesh<T>(obj) { buildGeometry(it) }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,8 +4,8 @@ import hep.dataforge.context.Context
|
||||
import hep.dataforge.meta.*
|
||||
import hep.dataforge.output.Output
|
||||
import hep.dataforge.vis.common.Colors
|
||||
import hep.dataforge.vis.common.VisualObject
|
||||
import hep.dataforge.vis.hmr.require
|
||||
import hep.dataforge.vis.spatial.VisualObject3D
|
||||
import info.laht.threekt.WebGLRenderer
|
||||
import info.laht.threekt.helpers.AxesHelper
|
||||
import info.laht.threekt.lights.AmbientLight
|
||||
@ -15,7 +15,7 @@ import kotlin.browser.window
|
||||
|
||||
private val elementResizeEvent = require("element-resize-event")
|
||||
|
||||
class ThreeOutput(val three: ThreePlugin, val meta: Meta = EmptyMeta) : Output<VisualObject> {
|
||||
class ThreeOutput(val three: ThreePlugin, val meta: Meta = EmptyMeta) : Output<VisualObject3D> {
|
||||
|
||||
override val context: Context get() = three.context
|
||||
|
||||
@ -58,7 +58,7 @@ class ThreeOutput(val three: ThreePlugin, val meta: Meta = EmptyMeta) : Output<V
|
||||
animate()
|
||||
}
|
||||
|
||||
override fun render(obj: VisualObject, meta: Meta) {
|
||||
override fun render(obj: VisualObject3D, meta: Meta) {
|
||||
scene.add(three.buildObject3D(obj))
|
||||
}
|
||||
}
|
||||
|
@ -5,8 +5,6 @@ import hep.dataforge.context.PluginFactory
|
||||
import hep.dataforge.context.PluginTag
|
||||
import hep.dataforge.context.content
|
||||
import hep.dataforge.meta.*
|
||||
import hep.dataforge.vis.common.VisualGroup
|
||||
import hep.dataforge.vis.common.VisualObject
|
||||
import hep.dataforge.vis.spatial.*
|
||||
import info.laht.threekt.cameras.Camera
|
||||
import info.laht.threekt.cameras.PerspectiveCamera
|
||||
@ -20,7 +18,7 @@ import kotlin.reflect.KClass
|
||||
class ThreePlugin : AbstractPlugin() {
|
||||
override val tag: PluginTag get() = Companion.tag
|
||||
|
||||
private val objectFactories = HashMap<KClass<out VisualObject>, ThreeFactory<*>>()
|
||||
private val objectFactories = HashMap<KClass<out VisualObject3D>, ThreeFactory<*>>()
|
||||
private val compositeFactory = ThreeCompositeFactory(this)
|
||||
|
||||
init {
|
||||
@ -31,15 +29,16 @@ class ThreePlugin : AbstractPlugin() {
|
||||
objectFactories[Cylinder::class] = ThreeCylinderFactory
|
||||
}
|
||||
|
||||
private fun findObjectFactory(type: KClass<out VisualObject>): ThreeFactory<*>? {
|
||||
private fun findObjectFactory(type: KClass<out VisualObject3D>): ThreeFactory<*>? {
|
||||
return objectFactories[type]
|
||||
?: context.content<ThreeFactory<*>>(ThreeFactory.TYPE).values.find { it.type == type }
|
||||
}
|
||||
|
||||
fun buildObject3D(obj: VisualObject): Object3D {
|
||||
fun buildObject3D(obj: VisualObject3D): Object3D {
|
||||
return when (obj) {
|
||||
is VisualGroup -> Group(obj.mapNotNull {
|
||||
is VisualGroup3D -> Group(obj.mapNotNull {
|
||||
try {
|
||||
it as VisualObject3D
|
||||
buildObject3D(it)
|
||||
} catch (ex: Throwable) {
|
||||
console.error(ex)
|
||||
|
@ -4,11 +4,7 @@ import hep.dataforge.meta.MetaItem
|
||||
import hep.dataforge.meta.float
|
||||
import hep.dataforge.meta.get
|
||||
import hep.dataforge.meta.node
|
||||
import hep.dataforge.vis.common.VisualObject
|
||||
import hep.dataforge.vis.spatial.rotationOrder
|
||||
import hep.dataforge.vis.spatial.rotationX
|
||||
import hep.dataforge.vis.spatial.rotationY
|
||||
import hep.dataforge.vis.spatial.rotationZ
|
||||
import hep.dataforge.vis.spatial.*
|
||||
import info.laht.threekt.core.BufferGeometry
|
||||
import info.laht.threekt.core.Geometry
|
||||
import info.laht.threekt.core.Object3D
|
||||
@ -25,7 +21,7 @@ fun Group(children: Collection<Object3D>) = info.laht.threekt.objects.Group().ap
|
||||
children.forEach { this.add(it) }
|
||||
}
|
||||
|
||||
val VisualObject.euler get() = Euler(rotationX, rotationY, rotationZ, rotationOrder.name)
|
||||
val VisualObject3D.euler get() = Euler(rotationX, rotationY, rotationZ, rotationOrder.name)
|
||||
|
||||
val MetaItem<*>.vector get() = Vector3(node["x"].float ?: 0f, node["y"].float ?: 0f, node["z"].float ?: 0f)
|
||||
|
||||
|
@ -2,14 +2,12 @@ package hep.dataforge.vis.spatial
|
||||
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.vis.common.VisualGroup
|
||||
import hep.dataforge.vis.common.VisualLeaf
|
||||
import hep.dataforge.vis.common.VisualObject
|
||||
|
||||
class Box(parent: VisualObject?, val xSize: Number, val ySize: Number, val zSize: Number, meta: Array<out Meta>) :
|
||||
VisualLeaf(parent, meta), Shape {
|
||||
VisualLeaf3D(parent, meta), Shape {
|
||||
|
||||
//TODO add helper for color configuration
|
||||
|
||||
override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) {
|
||||
val dx = xSize.toDouble() / 2
|
||||
val dy = ySize.toDouble() / 2
|
||||
@ -39,12 +37,11 @@ class Box(parent: VisualObject?, val xSize: Number, val ySize: Number, val zSize
|
||||
//fun VisualGroup.box(meta: Meta = EmptyMeta, action: Box.() -> Unit = {}) =
|
||||
// Box(this, meta).apply(action).also { add(it) }
|
||||
|
||||
fun VisualGroup.box(
|
||||
inline fun VisualGroup.box(
|
||||
xSize: Number,
|
||||
ySize: Number,
|
||||
zSize: Number,
|
||||
name: String? = null,
|
||||
vararg meta: Meta,
|
||||
action: Box.() -> Unit = {}
|
||||
) =
|
||||
Box(this, xSize, ySize, zSize, meta).apply(action).also { set(name, it) }
|
||||
) = Box(this, xSize, ySize, zSize, meta).apply(action).also { set(name, it) }
|
@ -1,10 +1,9 @@
|
||||
package hep.dataforge.vis.spatial
|
||||
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.seal
|
||||
import hep.dataforge.meta.isEmpty
|
||||
import hep.dataforge.meta.update
|
||||
import hep.dataforge.vis.common.VisualGroup
|
||||
import hep.dataforge.vis.common.VisualLeaf
|
||||
import hep.dataforge.vis.common.VisualObject
|
||||
|
||||
enum class CompositeType {
|
||||
@ -15,33 +14,38 @@ enum class CompositeType {
|
||||
|
||||
open class Composite(
|
||||
parent: VisualObject?,
|
||||
val first: VisualObject,
|
||||
val second: VisualObject,
|
||||
val first: VisualObject3D,
|
||||
val second: VisualObject3D,
|
||||
val type: CompositeType = CompositeType.UNION,
|
||||
meta: Array<out Meta>
|
||||
) : VisualLeaf(parent, meta)
|
||||
) : VisualLeaf3D(parent, meta)
|
||||
|
||||
fun VisualGroup.composite(
|
||||
type: CompositeType,
|
||||
name: String? = null,
|
||||
vararg meta: Meta,
|
||||
builder: VisualGroup.() -> Unit
|
||||
builder: VisualGroup3D.() -> Unit
|
||||
): Composite {
|
||||
val group = VisualGroup().apply(builder)
|
||||
val children = group.toList()
|
||||
val group = VisualGroup3D().apply(builder)
|
||||
val children = group.filterIsInstance<VisualObject3D>()
|
||||
if (children.size != 2) error("Composite requires exactly two children")
|
||||
val groupMeta = group.properties.seal()
|
||||
return Composite(this, children[0], children[1], type, meta).also {
|
||||
it.config.update(groupMeta)
|
||||
if( !group.config.isEmpty()) {
|
||||
it.config.update(group.config)
|
||||
}
|
||||
it.position = group.position
|
||||
it.rotation = group.rotation
|
||||
it.scale = group.scale
|
||||
it.material = group.material
|
||||
set(name, it)
|
||||
}
|
||||
}
|
||||
|
||||
fun VisualGroup.union(name: String? = null, vararg meta: Meta, builder: VisualGroup.() -> Unit) =
|
||||
fun VisualGroup3D.union(name: String? = null, vararg meta: Meta, builder: VisualGroup3D.() -> Unit) =
|
||||
composite(CompositeType.UNION, name, *meta, builder = builder)
|
||||
|
||||
fun VisualGroup.subtract(name: String? = null, vararg meta: Meta, builder: VisualGroup.() -> Unit) =
|
||||
fun VisualGroup3D.subtract(name: String? = null, vararg meta: Meta, builder: VisualGroup3D.() -> Unit) =
|
||||
composite(CompositeType.SUBTRACT, name, *meta, builder = builder)
|
||||
|
||||
fun VisualGroup.intersect(name: String? = null, vararg meta: Meta, builder: VisualGroup.() -> Unit) =
|
||||
fun VisualGroup3D.intersect(name: String? = null, vararg meta: Meta, builder: VisualGroup3D.() -> Unit) =
|
||||
composite(CompositeType.INTERSECT, name, *meta, builder = builder)
|
@ -2,10 +2,9 @@ package hep.dataforge.vis.spatial
|
||||
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.vis.common.VisualGroup
|
||||
import hep.dataforge.vis.common.VisualLeaf
|
||||
import hep.dataforge.vis.common.VisualObject
|
||||
|
||||
class Convex(parent: VisualObject?, val points: List<Point3D>, meta: Array<out Meta>) : VisualLeaf(parent, meta) {
|
||||
class Convex(parent: VisualObject?, val points: List<Point3D>, meta: Array<out Meta>) : VisualLeaf3D(parent, meta) {
|
||||
|
||||
|
||||
companion object {
|
||||
|
@ -2,7 +2,6 @@ package hep.dataforge.vis.spatial
|
||||
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.vis.common.VisualGroup
|
||||
import hep.dataforge.vis.common.VisualLeaf
|
||||
import hep.dataforge.vis.common.VisualObject
|
||||
import hep.dataforge.vis.common.number
|
||||
import kotlin.math.PI
|
||||
@ -10,11 +9,9 @@ import kotlin.math.PI
|
||||
/**
|
||||
* A cylinder or cut cone segment
|
||||
*/
|
||||
class Cylinder(parent: VisualObject?, radius: Number, height: Number, meta: Array<out Meta>) :
|
||||
VisualLeaf(parent, meta) {
|
||||
var radius by number(radius)
|
||||
class Cylinder(parent: VisualObject?, var radius: Number, var height: Number, meta: Array<out Meta>) :
|
||||
VisualLeaf3D(parent, meta) {
|
||||
var upperRadius by number(radius)
|
||||
var height by number(height)
|
||||
var startAngle by number(0.0)
|
||||
var angle by number(2 * PI)
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package hep.dataforge.vis.spatial
|
||||
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.vis.common.VisualGroup
|
||||
import hep.dataforge.vis.common.VisualLeaf
|
||||
import hep.dataforge.vis.common.VisualObject
|
||||
import kotlin.math.PI
|
||||
import kotlin.math.cos
|
||||
@ -33,7 +32,7 @@ fun Shape2DBuilder.polygon(vertices: Int, radius: Number) {
|
||||
|
||||
data class Layer(var x: Number, var y: Number, var z: Number, var scale: Number)
|
||||
|
||||
class Extruded(parent: VisualObject?, meta: Array<out Meta>) : VisualLeaf(parent, meta), Shape {
|
||||
class Extruded(parent: VisualObject?, meta: Array<out Meta>) : VisualLeaf3D(parent, meta), Shape {
|
||||
|
||||
var shape: List<Point2D> = ArrayList()
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
package hep.dataforge.vis.spatial
|
||||
|
||||
import hep.dataforge.meta.*
|
||||
import hep.dataforge.vis.common.VisualObject
|
||||
|
||||
data class Point2D(val x: Number, val y: Number) : MetaRepr {
|
||||
override fun toMeta(): Meta = buildMeta {
|
||||
@ -60,6 +59,6 @@ fun GeometryBuilder<*>.face4(
|
||||
face(vertex1, vertex3, vertex4, normal, meta)
|
||||
}
|
||||
|
||||
interface Shape : VisualObject {
|
||||
interface Shape : VisualObject3D {
|
||||
fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>)
|
||||
}
|
@ -2,17 +2,15 @@ package hep.dataforge.vis.spatial
|
||||
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.vis.common.VisualGroup
|
||||
import hep.dataforge.vis.common.VisualLeaf
|
||||
import hep.dataforge.vis.common.VisualObject
|
||||
import hep.dataforge.vis.common.double
|
||||
import hep.dataforge.vis.common.number
|
||||
import kotlin.math.PI
|
||||
|
||||
class Sphere(parent: VisualObject?, meta: Array<out Meta>) : VisualLeaf(parent, meta) {
|
||||
var radius by double(50.0)
|
||||
var phiStart by double(0.0)
|
||||
var phi by double(2 * PI)
|
||||
var thetaStart by double(0.0)
|
||||
var theta by double(PI)
|
||||
class Sphere(parent: VisualObject?, var radius: Number, meta: Array<out Meta>) : VisualLeaf3D(parent, meta) {
|
||||
var phiStart by number(0.0)
|
||||
var phi by number(2 * PI)
|
||||
var thetaStart by number(0.0)
|
||||
var theta by number(PI)
|
||||
}
|
||||
|
||||
fun VisualGroup.sphere(
|
||||
@ -22,8 +20,7 @@ fun VisualGroup.sphere(
|
||||
name: String? = null,
|
||||
vararg meta: Meta,
|
||||
action: Sphere.() -> Unit = {}
|
||||
) = Sphere(this, meta).apply(action).apply {
|
||||
this.radius = radius.toDouble()
|
||||
) = Sphere(this, radius, meta).apply(action).apply {
|
||||
this.phi = phi.toDouble()
|
||||
this.theta = theta.toDouble()
|
||||
}.also { set(name, it) }
|
@ -1,118 +1,115 @@
|
||||
package hep.dataforge.vis.spatial
|
||||
|
||||
import hep.dataforge.meta.*
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.plus
|
||||
import hep.dataforge.output.Output
|
||||
import hep.dataforge.vis.common.VisualGroup
|
||||
import hep.dataforge.vis.common.VisualLeaf
|
||||
import hep.dataforge.vis.common.VisualObject
|
||||
import hep.dataforge.vis.common.asName
|
||||
import hep.dataforge.vis.spatial.VisualObject3D.Companion.detailKey
|
||||
import hep.dataforge.vis.spatial.VisualObject3D.Companion.materialKey
|
||||
import hep.dataforge.vis.spatial.VisualObject3D.Companion.visibleKey
|
||||
|
||||
fun VisualGroup.group(key: String? = null, vararg meta: Meta, action: VisualGroup.() -> Unit = {}): VisualGroup =
|
||||
VisualGroup(this, meta).apply(action).also { set(key, it) }
|
||||
data class Value3(var x: Float = 0f, var y: Float = 0f, var z: Float = 0f)
|
||||
|
||||
interface VisualObject3D : VisualObject {
|
||||
var position: Value3
|
||||
var rotation: Value3
|
||||
var scale: Value3
|
||||
|
||||
fun Output<VisualObject>.render(meta: Meta = EmptyMeta, action: VisualGroup.() -> Unit) =
|
||||
render(VisualGroup().apply(action), meta)
|
||||
fun setProperty(name: Name, value: Any?)
|
||||
fun getProperty(name: Name, inherit: Boolean = true): MetaItem<*>?
|
||||
|
||||
//TODO replace properties by containers?
|
||||
companion object {
|
||||
val materialKey = "material".asName()
|
||||
val visibleKey = "visible".asName()
|
||||
val detailKey = "detail".asName()
|
||||
|
||||
object PropertyNames3D {
|
||||
val x = "x".asName()
|
||||
val y = "y".asName()
|
||||
val z = "z".asName()
|
||||
val x = "x".asName()
|
||||
val y = "y".asName()
|
||||
val z = "z".asName()
|
||||
|
||||
val position = "pos".asName()
|
||||
val position = "pos".asName()
|
||||
|
||||
val xPos = position + x
|
||||
val yPos = position + y
|
||||
val zPos = position + z
|
||||
val xPos = position + x
|
||||
val yPos = position + y
|
||||
val zPos = position + z
|
||||
|
||||
val rotation = "rotation".asName()
|
||||
val rotation = "rotation".asName()
|
||||
|
||||
val xRotation = rotation + x
|
||||
val yRotation = rotation + y
|
||||
val zRotation = rotation + z
|
||||
val xRotation = rotation + x
|
||||
val yRotation = rotation + y
|
||||
val zRotation = rotation + z
|
||||
|
||||
val rotationOrder = rotation + "order"
|
||||
val rotationOrder = rotation + "order"
|
||||
|
||||
val scale = "scale".asName()
|
||||
val scale = "scale".asName()
|
||||
|
||||
val xScale = scale + x
|
||||
val yScale = scale + y
|
||||
val zScale = scale + z
|
||||
val xScale = scale + x
|
||||
val yScale = scale + y
|
||||
val zScale = scale + z
|
||||
}
|
||||
}
|
||||
|
||||
open class VisualLeaf3D(parent: VisualObject?, tagRefs: Array<out Meta>) : VisualLeaf(parent, tagRefs), VisualObject3D {
|
||||
override var position: Value3 = Value3()
|
||||
override var rotation: Value3 = Value3()
|
||||
override var scale: Value3 = Value3(1f, 1f, 1f)
|
||||
|
||||
private var _config: Config? = null
|
||||
override val config: Config get() = _config ?: Config().also { _config = it }
|
||||
|
||||
override fun setProperty(name: Name, value: Any?) {
|
||||
config[name] = value
|
||||
}
|
||||
|
||||
override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? {
|
||||
return if (inherit) {
|
||||
config[name] ?: (parent as? VisualObject3D)?.getProperty(name, inherit) ?: parent?.properties[name]
|
||||
} else {
|
||||
_config?.get(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class VisualGroup3D(
|
||||
parent: VisualObject? = null,
|
||||
tagRefs: Array<out Meta> = emptyArray()
|
||||
) : VisualGroup(parent, tagRefs), VisualObject3D {
|
||||
|
||||
override var position: Value3 = Value3()
|
||||
override var rotation: Value3 = Value3()
|
||||
override var scale: Value3 = Value3(1f, 1f, 1f)
|
||||
|
||||
private var _config: Config? = null
|
||||
override val config: Config get() = _config ?: Config().also { _config = it }
|
||||
|
||||
override fun setProperty(name: Name, value: Any?) {
|
||||
config[name] = value
|
||||
}
|
||||
|
||||
override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? {
|
||||
return if (inherit) {
|
||||
config[name] ?: (parent as? VisualObject3D)?.getProperty(name, inherit) ?: parent?.properties[name]
|
||||
} else {
|
||||
_config?.get(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun VisualGroup.group(key: String? = null, vararg meta: Meta, action: VisualGroup3D.() -> Unit = {}): VisualGroup3D =
|
||||
VisualGroup3D(this, meta).apply(action).also { set(key, it) }
|
||||
|
||||
|
||||
fun Output<VisualObject>.render(meta: Meta = EmptyMeta, action: VisualGroup3D.() -> Unit) =
|
||||
render(VisualGroup3D().apply(action), meta)
|
||||
|
||||
|
||||
// Common properties
|
||||
|
||||
/**
|
||||
* Visibility property. Inherited from parent
|
||||
*/
|
||||
var VisualObject.visible
|
||||
get() = properties["visible"].boolean ?: true
|
||||
set(value) {
|
||||
config["visible"] = value
|
||||
}
|
||||
|
||||
// 3D Object position
|
||||
|
||||
/**
|
||||
* x position property relative to parent. Not inherited
|
||||
*/
|
||||
var VisualObject.x
|
||||
get() = config[PropertyNames3D.xPos].number ?: 0.0
|
||||
set(value) {
|
||||
config[PropertyNames3D.xPos] = value
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* y position property. Not inherited
|
||||
*/
|
||||
var VisualObject.y
|
||||
get() = config[PropertyNames3D.yPos].number ?: 0.0
|
||||
set(value) {
|
||||
config[PropertyNames3D.yPos] = value
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* z position property. Not inherited
|
||||
*/
|
||||
var VisualObject.z
|
||||
get() = config[PropertyNames3D.zPos].number ?: 0.0
|
||||
set(value) {
|
||||
config[PropertyNames3D.zPos] = value
|
||||
}
|
||||
|
||||
// 3D Object rotation
|
||||
|
||||
|
||||
/**
|
||||
* x rotation relative to parent. Not inherited
|
||||
*/
|
||||
var VisualObject.rotationX
|
||||
get() = config[PropertyNames3D.xRotation].number ?: 0.0
|
||||
set(value) {
|
||||
config[PropertyNames3D.xRotation] = value
|
||||
}
|
||||
|
||||
/**
|
||||
* y rotation relative to parent. Not inherited
|
||||
*/
|
||||
var VisualObject.rotationY
|
||||
get() = config[PropertyNames3D.yRotation].number ?: 0.0
|
||||
set(value) {
|
||||
config[PropertyNames3D.yRotation] = value
|
||||
}
|
||||
|
||||
/**
|
||||
* z rotation relative to parent. Not inherited
|
||||
*/
|
||||
var VisualObject.rotationZ
|
||||
get() = config[PropertyNames3D.zRotation].number ?: 0.0
|
||||
set(value) {
|
||||
config[PropertyNames3D.zRotation] = value
|
||||
}
|
||||
|
||||
enum class RotationOrder {
|
||||
XYZ,
|
||||
@ -124,53 +121,41 @@ enum class RotationOrder {
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotation order. Not inherited
|
||||
* Rotation order
|
||||
*/
|
||||
var VisualObject.rotationOrder: RotationOrder
|
||||
get() = config[PropertyNames3D.rotationOrder].enum<RotationOrder>() ?: RotationOrder.XYZ
|
||||
set(value) {
|
||||
config[PropertyNames3D.rotationOrder] = value
|
||||
}
|
||||
var VisualObject3D.rotationOrder: RotationOrder
|
||||
get() = getProperty(VisualObject3D.rotationOrder).enum<RotationOrder>() ?: RotationOrder.XYZ
|
||||
set(value) = setProperty(VisualObject3D.rotationOrder, value)
|
||||
|
||||
// 3D object scale
|
||||
|
||||
/**
|
||||
* X scale. Not inherited
|
||||
*/
|
||||
var VisualObject.scaleX
|
||||
get() = config[PropertyNames3D.xScale].number ?: 1.0
|
||||
set(value) {
|
||||
config[PropertyNames3D.xScale] = value
|
||||
}
|
||||
|
||||
/**
|
||||
* Y scale. Not inherited
|
||||
*/
|
||||
var VisualObject.scaleY
|
||||
get() = config[PropertyNames3D.yScale].number ?: 1.0
|
||||
set(value) {
|
||||
config[PropertyNames3D.yScale] = value
|
||||
}
|
||||
|
||||
/**
|
||||
* Z scale. Not inherited
|
||||
*/
|
||||
var VisualObject.scaleZ
|
||||
get() = config[PropertyNames3D.zScale].number ?: 1.0
|
||||
set(value) {
|
||||
config[PropertyNames3D.zScale] = value
|
||||
}
|
||||
|
||||
//TODO add inherited scale
|
||||
|
||||
/**
|
||||
* Preferred number of polygons for displaying the object. If not defined, uses shape or renderer default
|
||||
*/
|
||||
var VisualObject.detail: Int?
|
||||
get() = properties["detail"]?.int
|
||||
set(value) {
|
||||
config["detail"] = value
|
||||
}
|
||||
var VisualObject3D.detail: Int?
|
||||
get() = getProperty(detailKey).int
|
||||
set(value) = setProperty(detailKey, value)
|
||||
|
||||
var VisualObject3D.material: Meta?
|
||||
get() = getProperty(materialKey).node
|
||||
set(value) = setProperty(materialKey, value)
|
||||
|
||||
var VisualObject3D.visible: Boolean?
|
||||
get() = getProperty(visibleKey).boolean
|
||||
set(value) = setProperty(visibleKey, value)
|
||||
|
||||
fun VisualObject3D.color(rgb: Int) {
|
||||
material = buildMeta { "color" to rgb }
|
||||
}
|
||||
|
||||
fun VisualObject3D.material(builder: MetaBuilder.() -> Unit) {
|
||||
material = buildMeta(builder)
|
||||
}
|
||||
|
||||
fun VisualObject3D.color(r: Int, g: Int, b: Int) = material {
|
||||
"red" to r
|
||||
"green" to g
|
||||
"blue" to b
|
||||
}
|
||||
|
||||
object World {
|
||||
const val CAMERA_INITIAL_DISTANCE = -500.0
|
||||
|
@ -0,0 +1,37 @@
|
||||
package hep.dataforge.vis.spatial
|
||||
|
||||
var VisualObject3D.x: Number
|
||||
get() = position.x
|
||||
set(value) {position.x = value.toFloat()}
|
||||
|
||||
var VisualObject3D.y: Number
|
||||
get() = position.y
|
||||
set(value) {position.y = value.toFloat()}
|
||||
|
||||
var VisualObject3D.z: Number
|
||||
get() = position.z
|
||||
set(value) {position.z = value.toFloat()}
|
||||
|
||||
var VisualObject3D.rotationX: Number
|
||||
get() = rotation.x
|
||||
set(value) {rotation.x = value.toFloat()}
|
||||
|
||||
var VisualObject3D.rotationY: Number
|
||||
get() = rotation.y
|
||||
set(value) {rotation.y = value.toFloat()}
|
||||
|
||||
var VisualObject3D.rotationZ: Number
|
||||
get() = rotation.z
|
||||
set(value) {rotation.z = value.toFloat()}
|
||||
|
||||
var VisualObject3D.scaleX: Number
|
||||
get() = scale.x
|
||||
set(value) {scale.x = value.toFloat()}
|
||||
|
||||
var VisualObject3D.scaleY: Number
|
||||
get() = scale.y
|
||||
set(value) {scale.y = value.toFloat()}
|
||||
|
||||
var VisualObject3D.scaleZ: Number
|
||||
get() = scale.z
|
||||
set(value) {scale.z = value.toFloat()}
|
@ -18,7 +18,7 @@ class GroupTest {
|
||||
rotationY = PI / 4
|
||||
}
|
||||
box(100, 100, 100)
|
||||
color {
|
||||
material {
|
||||
"color" to Colors.lightgreen
|
||||
"opacity" to 0.3
|
||||
}
|
||||
@ -31,7 +31,7 @@ class GroupTest {
|
||||
}
|
||||
box(100, 100, 100)
|
||||
y = 300
|
||||
color(Colors.red)
|
||||
material(Colors.red)
|
||||
}
|
||||
subtract{
|
||||
box(100, 100, 100) {
|
||||
@ -41,7 +41,7 @@ class GroupTest {
|
||||
}
|
||||
box(100, 100, 100)
|
||||
y = -300
|
||||
color(Colors.blue)
|
||||
material(Colors.blue)
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user