Performance optimization

This commit is contained in:
Alexander Nozik 2019-07-30 17:45:34 +03:00
parent e31ad5ece1
commit 997a5a8e60
27 changed files with 510 additions and 426 deletions

View File

@ -1,11 +1,5 @@
package hep.dataforge.vis.common 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 * Taken from https://github.com/markaren/three.kt/blob/master/threejs-wrapper/src/main/kotlin/info/laht/threekt/math/ColorConstants.kt
*/ */
@ -181,23 +175,3 @@ object Colors {
const val yellow = 0xFFFF00 const val yellow = 0xFFFF00
const val yellowgreen = 0x9ACD32 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
}

View File

@ -5,22 +5,12 @@ import hep.dataforge.meta.Laminate
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.provider.Provider 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 import kotlin.collections.set
/** /**
* A display group which allows both named and unnamed children * 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() override val parent: VisualObject? = null, tagRefs: Array<out Meta> = emptyArray()
) : VisualObject, Iterable<VisualObject>, Provider { ) : VisualObject, Iterable<VisualObject>, Provider {
@ -30,7 +20,9 @@ class VisualGroup(
override val defaultTarget: String get() = VisualObject.TYPE override val defaultTarget: String get() = VisualObject.TYPE
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)
}
override fun iterator(): Iterator<VisualObject> = (namedChildren.values + unnamedChildren).iterator() override fun iterator(): Iterator<VisualObject> = (namedChildren.values + unnamedChildren).iterator()

View File

@ -78,9 +78,11 @@ open class VisualLeaf(
final override val parent: VisualObject?, final override val parent: VisualObject?,
tagRefs: Array<out Meta> tagRefs: Array<out Meta>
) : VisualObject { ) : 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 { internal fun combineProperties(parent: VisualObject?, config: Config, tagRefs: Array<out Meta>): Laminate {

View File

@ -5,71 +5,72 @@ import scientifik.gdml.GDMLRotation
import scientifik.gdml.GDMLSolid import scientifik.gdml.GDMLSolid
import kotlin.math.PI import kotlin.math.PI
enum class LUnit(val value: Double) { enum class LUnit(val value: Float) {
MM(1.0), MM(1f),
CM(10.0), CM(10f),
M(1000.0) M(1000f)
} }
enum class AUnit(val value: Double) { enum class AUnit(val value: Float) {
DEG(PI / 180), DEG(PI.toFloat() / 180),
RAD(1.0), DEGREE(PI.toFloat() / 180),
RADIAN(1.0) RAD(1f),
RADIAN(1f)
} }
fun GDMLPosition.unit(): LUnit = LUnit.valueOf(unit.toUpperCase()) fun GDMLPosition.unit(): LUnit = LUnit.valueOf(unit.toUpperCase())
fun GDMLPosition.x(unit: LUnit): Double = if (unit.name == this.unit) { fun GDMLPosition.x(unit: LUnit): Float = if (unit.name == this.unit) {
x.toDouble() x.toFloat()
} else { } else {
x.toDouble() / unit.value * unit().value x.toFloat() / unit.value * unit().value
} }
fun GDMLPosition.y(unit: LUnit): Double = if (unit.name == this.unit) { fun GDMLPosition.y(unit: LUnit): Float = if (unit.name == this.unit) {
y.toDouble() y.toFloat()
} else { } else {
y.toDouble() / unit.value * unit().value y.toFloat() / unit.value * unit().value
} }
fun GDMLPosition.z(unit: LUnit): Double = if (unit.name == this.unit) { fun GDMLPosition.z(unit: LUnit): Float = if (unit.name == this.unit) {
z.toDouble() z.toFloat()
} else { } else {
z.toDouble() / unit.value * unit().value z.toFloat() / unit.value * unit().value
} }
fun GDMLRotation.unit(): AUnit = AUnit.valueOf(unit.toUpperCase()) fun GDMLRotation.unit(): AUnit = AUnit.valueOf(unit.toUpperCase())
fun GDMLRotation.x(unit: AUnit = AUnit.RAD): Double = if (unit.name == this.unit) { fun GDMLRotation.x(unit: AUnit = AUnit.RAD): Float = if (unit.name == this.unit) {
x.toDouble() x.toFloat()
} else { } 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) { fun GDMLRotation.y(unit: AUnit = AUnit.RAD): Float = if (unit.name == this.unit) {
y.toDouble() y.toFloat()
} else { } 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) { fun GDMLRotation.z(unit: AUnit = AUnit.RAD): Float = if (unit.name == this.unit) {
z.toDouble() z.toFloat()
} else { } else {
z.toDouble() / unit.value * unit().value z.toFloat() / unit.value * unit().value
} }
fun GDMLSolid.lscale(unit: LUnit): Double { fun GDMLSolid.lscale(unit: LUnit): Float {
val solidUnit = lunit?.let { LUnit.valueOf(it.toUpperCase()) } ?: return 1.0 val solidUnit = lunit?.let { LUnit.valueOf(it.toUpperCase()) } ?: return 1f
return if (solidUnit == unit) { return if (solidUnit == unit) {
1.0 1f
} else { } else {
solidUnit.value / unit.value solidUnit.value / unit.value
} }
} }
fun GDMLSolid.ascale(unit: AUnit = AUnit.RAD): Double { fun GDMLSolid.ascale(unit: AUnit = AUnit.RAD): Float {
val solidUnit = aunit?.let { AUnit.valueOf(it.toUpperCase()) } ?: return 1.0 val solidUnit = aunit?.let { AUnit.valueOf(it.toUpperCase()) } ?: return 1f
return if (solidUnit == unit) { return if (solidUnit == unit) {
1.0 1f
} else { } else {
solidUnit.value / unit.value solidUnit.value / unit.value
} }

View File

@ -2,9 +2,7 @@ package hep.dataforge.vis.spatial.gdml
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.meta.buildMeta import hep.dataforge.meta.buildMeta
import hep.dataforge.vis.common.VisualGroup import hep.dataforge.meta.builder
import hep.dataforge.vis.common.VisualObject
import hep.dataforge.vis.common.color
import hep.dataforge.vis.spatial.* import hep.dataforge.vis.spatial.*
import scientifik.gdml.* import scientifik.gdml.*
import kotlin.math.cos import kotlin.math.cos
@ -12,41 +10,87 @@ import kotlin.math.sin
import kotlin.random.Random 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, lUnit: LUnit,
pos: GDMLPosition? = null, pos: GDMLPosition? = null,
rotation: GDMLRotation? = null, rotation: GDMLRotation? = null,
scale: GDMLScale? = null scale: GDMLScale? = null
): VisualObject = apply { ): VisualObject3D = apply {
pos?.let { pos?.let {
x = pos.x(lUnit) this@withPosition.position.x = pos.x(lUnit)
y = pos.y(lUnit) this@withPosition.position.y = pos.y(lUnit)
z = pos.z(lUnit) this@withPosition.position.z = pos.z(lUnit)
} }
rotation?.let { rotation?.let {
rotationX = rotation.x() this@withPosition.rotation.x = rotation.x()
rotationY = rotation.y() this@withPosition.rotation.y = rotation.y()
rotationZ = rotation.z() this@withPosition.rotation.z = rotation.z()
} }
scale?.let { scale?.let {
scaleX = scale.x this@withPosition.scale.x = scale.x.toFloat()
scaleY = scale.y this@withPosition.scale.y = scale.y.toFloat()
scaleZ = scale.z this@withPosition.scale.z = scale.z.toFloat()
} }
//TODO convert units if needed //TODO convert units if needed
} }
private inline operator fun Number.times(d: Double) = toDouble() * d private inline operator fun Number.times(d: Double) = toDouble() * d
private inline operator fun Number.times(f: Float) = toFloat() * f
private fun VisualGroup.addSolid( private fun VisualGroup3D.addSolid(
root: GDML, context: GDMLTransformer,
solid: GDMLSolid, solid: GDMLSolid,
lUnit: LUnit,
name: String? = null, name: String? = null,
block: VisualObject.() -> Unit = {} block: VisualObject3D.() -> Unit = {}
): VisualObject { ): VisualObject3D {
val lScale = solid.lscale(lUnit) context.solidAdded(solid)
val lScale = solid.lscale(context.lUnit)
val aScale = solid.ascale() val aScale = solid.ascale()
return when (solid) { return when (solid) {
is GDMLBox -> box(solid.x * lScale, solid.y * lScale, solid.z * lScale, name) is GDMLBox -> box(solid.x * lScale, solid.y * lScale, solid.z * lScale, name)
@ -71,14 +115,14 @@ private fun VisualGroup.addSolid(
} }
is GDMLScaledSolid -> { is GDMLScaledSolid -> {
//Add solid with modified scale //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") ?: error("Solid with tag ${solid.solidref.ref} for scaled solid ${solid.name} not defined")
addSolid(root, innerSolid, lUnit) { addSolid(context, innerSolid) {
block() block()
scaleX = scaleX.toDouble() * solid.scale.x.toDouble() scale.x *= solid.scale.x.toFloat()
scaleY = scaleY.toDouble() * solid.scale.y.toDouble() scale.y *= solid.scale.y.toFloat()
scaleZ = scaleZ.toDouble() * solid.scale.z.toDouble() scale.z = solid.scale.z.toFloat()
} }
} }
is GDMLSphere -> sphere(solid.rmax * lScale, solid.deltaphi * aScale, solid.deltatheta * aScale, name) { is GDMLSphere -> sphere(solid.rmax * lScale, solid.deltaphi * aScale, solid.deltatheta * aScale, name) {
@ -102,8 +146,8 @@ private fun VisualGroup.addSolid(
} }
} }
is GDMLBoolSolid -> { is GDMLBoolSolid -> {
val first = solid.first.resolve(root) ?: error("") val first = solid.first.resolve(context.root) ?: error("")
val second = solid.second.resolve(root) ?: error("") val second = solid.second.resolve(context.root) ?: error("")
val type: CompositeType = when (solid) { val type: CompositeType = when (solid) {
is GDMLUnion -> CompositeType.UNION is GDMLUnion -> CompositeType.UNION
is GDMLSubtraction -> CompositeType.SUBTRACT is GDMLSubtraction -> CompositeType.SUBTRACT
@ -111,97 +155,114 @@ private fun VisualGroup.addSolid(
} }
return composite(type, name) { return composite(type, name) {
addSolid(root, first, lUnit) { addSolid(context, first) {
withPosition(lUnit, solid.resolveFirstPosition(root), solid.resolveFirstRotation(root), null) withPosition(
context.lUnit,
solid.resolveFirstPosition(context.root),
solid.resolveFirstRotation(context.root),
null
)
} }
addSolid(root, second, lUnit) { addSolid(context, second) {
withPosition(lUnit, solid.resolvePosition(root), solid.resolveRotation(root), null) withPosition(
context.lUnit,
solid.resolvePosition(context.root),
solid.resolveRotation(context.root),
null
)
} }
} }
} }
}.apply(block) }.apply(block)
} }
private fun VisualGroup.addPhysicalVolume( private fun VisualGroup3D.addPhysicalVolume(
root: GDML, context: GDMLTransformer,
physVolume: GDMLPhysVolume, physVolume: GDMLPhysVolume
lUnit: LUnit,
resolveColor: GDMLMaterial.() -> Meta
) { ) {
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") ?: error("Volume with ref ${physVolume.volumeref.ref} could not be resolved")
addVolume( if (context.acceptGroup(volume)) {
root,
this[physVolume.name] = volume(
context,
volume, volume,
lUnit, physVolume.resolvePosition(context.root),
physVolume.resolvePosition(root), physVolume.resolveRotation(context.root),
physVolume.resolveRotation(root), physVolume.resolveScale(context.root)
physVolume.resolveScale(root),
resolveColor
) )
} }
}
private fun VisualGroup.addDivisionVolume( private fun VisualGroup3D.addDivisionVolume(
root: GDML, context: GDMLTransformer,
divisionVolume: GDMLDivisionVolume, divisionVolume: GDMLDivisionVolume
lUnit: LUnit,
resolveColor: GDMLMaterial.() -> Meta
) { ) {
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") ?: error("Volume with ref ${divisionVolume.volumeref.ref} could not be resolved")
//TODO add divisions //TODO add divisions
addVolume( add(
root, volume(
volume, context,
lUnit, volume
resolveColor = resolveColor )
) )
} }
private fun VisualGroup.addVolume( private fun VisualGroup3D.addVolume(
root: GDML, context: GDMLTransformer,
group: GDMLGroup, group: GDMLGroup,
lUnit: LUnit,
position: GDMLPosition? = null, position: GDMLPosition? = null,
rotation: GDMLRotation? = null, rotation: GDMLRotation? = null,
scale: GDMLScale? = null, scale: GDMLScale? = null
resolveColor: GDMLMaterial.() -> Meta
) { ) {
this[group.name] = volume(context, group, position, rotation, scale)
}
group(group.name) { private fun volume(
withPosition(lUnit, position, rotation, scale) 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) { 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") ?: error("Solid with tag ${group.solidref.ref} for volume ${group.name} not defined")
val material = group.materialref.resolve(root) ?: GDMLElement(group.materialref.ref) val material = group.materialref.resolve(context.root) ?: GDMLElement(group.materialref.ref)
//?: error("Material with tag ${group.materialref.ref} for volume ${group.name} not defined")
addSolid(root, solid, lUnit, solid.name) { if (context.acceptSolid(solid)) {
color(material.resolveColor()) addSolid(context, solid, solid.name) {
this.material = context.resolveColor(group, material, solid)
}
} }
when (val vol = group.placement) { when (val vol = group.placement) {
is GDMLPhysVolume -> addPhysicalVolume(root, vol, lUnit, resolveColor) is GDMLPhysVolume -> addPhysicalVolume(context, vol)
is GDMLDivisionVolume -> addDivisionVolume(root, vol, lUnit, resolveColor) is GDMLDivisionVolume -> addDivisionVolume(context, vol)
} }
} }
group.physVolumes.forEach { physVolume -> group.physVolumes.forEach { physVolume ->
addPhysicalVolume(root, physVolume, lUnit, resolveColor) addPhysicalVolume(context, physVolume)
} }
} }
} }
fun GDML.toVisual(lUnit: LUnit = LUnit.MM): VisualGroup { typealias ColorResolver = GDMLGroup?.(GDMLMaterial, GDMLSolid?) -> Meta
val cache = HashMap<GDMLMaterial, Meta>()
val random = Random(111)
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() } } fun GDML.toVisual(block: GDMLTransformer.() -> Unit = {}): VisualGroup3D {
val context = GDMLTransformer(this).apply(block)
return volume(context, world).also{
context.finished()
}
} }

View File

@ -88,7 +88,14 @@ private class GDMLDemoApp : ApplicationBase() {
launch { message("Loading GDML") } launch { message("Loading GDML") }
val gdml = GDML.format.parse(GDML.serializer(), it) val gdml = GDML.format.parse(GDML.serializer(), it)
launch { message("Converting GDML into DF-VIS format") } 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") } launch { message("Rendering") }
val output = three.output(canvas) val output = three.output(canvas)
output.render(visual) output.render(visual)

View File

@ -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()
}

View File

@ -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()
}
}
}

View File

@ -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)
}
}

View File

@ -1,9 +1,7 @@
package hep.dataforge.vis.spatial.demo package hep.dataforge.vis.spatial.demo
import hep.dataforge.context.ContextBuilder import hep.dataforge.context.ContextBuilder
import hep.dataforge.meta.number
import hep.dataforge.vis.common.Colors import hep.dataforge.vis.common.Colors
import hep.dataforge.vis.common.color
import hep.dataforge.vis.hmr.ApplicationBase import hep.dataforge.vis.hmr.ApplicationBase
import hep.dataforge.vis.hmr.startApplication import hep.dataforge.vis.hmr.startApplication
import hep.dataforge.vis.spatial.* import hep.dataforge.vis.spatial.*
@ -42,6 +40,7 @@ private class ThreeDemoApp : ApplicationBase() {
box(100, 100, 100) { box(100, 100, 100) {
z = 110.0 z = 110.0
} }
box(100, 100, 100) { box(100, 100, 100) {
visible = false visible = false
x = 110.0 x = 110.0
@ -51,32 +50,32 @@ private class ThreeDemoApp : ApplicationBase() {
GlobalScope.launch { GlobalScope.launch {
while (isActive) { while (isActive) {
delay(500) delay(500)
visible = !visible visible = !(visible ?: false)
} }
} }
} }
} }
var material by group.config.number(1530).int
GlobalScope.launch { GlobalScope.launch {
val random = Random(111) val random = Random(111)
while (isActive) { while (isActive) {
delay(1000) delay(1000)
material = random.nextInt(0, Int.MAX_VALUE) group.color(random.nextInt(0, Int.MAX_VALUE))
} }
} }
} }
// demo("jsroot", "JSROOT cube") { demo("rotation", "Rotations") {
// jsRootGeometry { box(100, 100, 100)
// y = 110.0 group {
// shape = box(50, 50, 50) x = 200
// color(Colors.lightcoral) rotationY = PI / 4
// rotationX = PI / 4 box(100, 100, 100) {
// rotationY = PI / 4 rotationZ = PI / 4
// } color(Colors.red)
// } }
}
}
demo("extrude", "extruded shape") { demo("extrude", "extruded shape") {
extrude { extrude {
@ -86,10 +85,9 @@ private class ThreeDemoApp : ApplicationBase() {
for (i in 0..100) { for (i in 0..100) {
layer(i * 5, 20 * sin(2 * PI / 100 * i), 20 * cos(2 * PI / 100 * i)) 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") { demo("CSG", "CSG operations") {
composite(CompositeType.UNION) { composite(CompositeType.UNION) {
@ -97,7 +95,7 @@ private class ThreeDemoApp : ApplicationBase() {
z = 50 z = 50
} }
sphere(50) sphere(50)
color { material {
"color" to Colors.lightgreen "color" to Colors.lightgreen
"opacity" to 0.3 "opacity" to 0.3
} }

View File

@ -12,8 +12,8 @@ import hep.dataforge.names.Name
import hep.dataforge.names.toName import hep.dataforge.names.toName
import hep.dataforge.output.Output import hep.dataforge.output.Output
import hep.dataforge.output.OutputManager import hep.dataforge.output.OutputManager
import hep.dataforge.vis.common.VisualGroup
import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.common.VisualObject
import hep.dataforge.vis.spatial.VisualGroup3D
import hep.dataforge.vis.spatial.render import hep.dataforge.vis.spatial.render
import hep.dataforge.vis.spatial.three.ThreeOutput import hep.dataforge.vis.spatial.three.ThreeOutput
import hep.dataforge.vis.spatial.three.ThreePlugin 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 { val meta = buildMeta {
"title" to title "title" to title
} }

View File

@ -10,8 +10,9 @@ import info.laht.threekt.math.Color
object Materials { object Materials {
val DEFAULT_COLOR = Color(Colors.darkgreen)
val DEFAULT = MeshPhongMaterial().apply { 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 * Infer Three material based on meta item
*/ */
fun MetaItem<*>?.material(): Material { fun Meta?.jsMaterial(): Material {
return when (this) { return if(this == null){
null -> Materials.DEFAULT Materials.DEFAULT
is MetaItem.ValueItem -> MeshBasicMaterial().apply { } else
color = this@material.color() //TODO add more oprions for material
} MeshBasicMaterial().apply {
is MetaItem.NodeItem -> MeshBasicMaterial().apply { color = get("color")?.color()?: Materials.DEFAULT_COLOR
(node["color"] ?: this@material).let { color = it.color() } opacity = get("opacity")?.double ?: 1.0
opacity = node["opacity"]?.double ?: 1.0 transparent = get("transparent").boolean ?: (opacity < 1.0)
transparent = node["transparent"].boolean ?: (opacity < 1.0)
//node["specularColor"]?.let { specular = it.color() } //node["specularColor"]?.let { specular = it.color() }
side = 2 side = 2
} }
}
} }

View File

@ -4,11 +4,12 @@ import hep.dataforge.vis.spatial.Cylinder
import hep.dataforge.vis.spatial.detail import hep.dataforge.vis.spatial.detail
import info.laht.threekt.core.BufferGeometry import info.laht.threekt.core.BufferGeometry
import info.laht.threekt.geometries.CylinderBufferGeometry import info.laht.threekt.geometries.CylinderBufferGeometry
import kotlin.math.PI
import kotlin.math.pow import kotlin.math.pow
object ThreeCylinderFactory : MeshThreeFactory<Cylinder>(Cylinder::class) { object ThreeCylinderFactory : MeshThreeFactory<Cylinder>(Cylinder::class) {
override fun buildGeometry(obj: Cylinder): BufferGeometry { override fun buildGeometry(obj: Cylinder): BufferGeometry {
return obj.detail?.let { val cylinder = obj.detail?.let {
val segments = it.toDouble().pow(0.5).toInt() val segments = it.toDouble().pow(0.5).toInt()
CylinderBufferGeometry( CylinderBufferGeometry(
radiusTop = obj.upperRadius, radiusTop = obj.upperRadius,
@ -28,5 +29,6 @@ object ThreeCylinderFactory : MeshThreeFactory<Cylinder>(Cylinder::class) {
thetaStart = obj.startAngle, thetaStart = obj.startAngle,
thetaLength = obj.angle thetaLength = obj.angle
) )
return cylinder.rotateX(PI/2)
} }
} }

View File

@ -2,9 +2,10 @@ package hep.dataforge.vis.spatial.three
import hep.dataforge.meta.boolean import hep.dataforge.meta.boolean
import hep.dataforge.meta.get import hep.dataforge.meta.get
import hep.dataforge.meta.int
import hep.dataforge.meta.node
import hep.dataforge.names.startsWith import hep.dataforge.names.startsWith
import hep.dataforge.provider.Type import hep.dataforge.provider.Type
import hep.dataforge.vis.common.VisualObject
import hep.dataforge.vis.common.onChange import hep.dataforge.vis.common.onChange
import hep.dataforge.vis.spatial.* import hep.dataforge.vis.spatial.*
import hep.dataforge.vis.spatial.three.ThreeFactory.Companion.TYPE 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 info.laht.threekt.objects.Mesh
import kotlin.reflect.KClass import kotlin.reflect.KClass
internal val VisualObject.material get() = properties["material"].material()
/** /**
* Builder and updater for three.js object * Builder and updater for three.js object
*/ */
@Type(TYPE) @Type(TYPE)
interface ThreeFactory<T : VisualObject> { interface ThreeFactory<T : VisualObject3D> {
val type: KClass<out T> val type: KClass<out T>
@ -33,46 +32,50 @@ interface ThreeFactory<T : VisualObject> {
companion object { companion object {
const val TYPE = "threeFactory" 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) val geometry = geometryBuilder(obj)
//JS sometimes tries to pass Geometry as BufferGeometry //JS sometimes tries to pass Geometry as BufferGeometry
@Suppress("USELESS_IS_CHECK") if (geometry !is BufferGeometry) error("BufferGeometry expected") @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 //inherited edges definition, enabled by default
if (obj.properties["edges.enabled"].boolean != false) { 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)) mesh.add(LineSegments(EdgesGeometry(mesh.geometry as BufferGeometry), material))
} }
//inherited wireframe definition, disabled by default //inherited wireframe definition, disabled by default
if (obj.properties["wireframe.enabled"].boolean == true) { 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)) mesh.add(LineSegments(WireframeGeometry(mesh.geometry as BufferGeometry), material))
} }
//set position for meseh //set position for mesh
mesh.updatePosition(obj) mesh.updatePosition(obj)
obj.config["layer"].int?.let {
mesh.layers.set(it)
}
//add listener to object properties //add listener to object properties
obj.onChange(this) { name, _, _ -> obj.onChange(this) { name, _, _ ->
if (name.toString() == "material") { if (name.startsWith(VisualObject3D.materialKey)) {
//updated material //updated material
mesh.material = obj.material mesh.material = obj.material.jsMaterial()
} else if ( } else if (
name.startsWith(PropertyNames3D.position) || name.startsWith(VisualObject3D.position) ||
name.startsWith(PropertyNames3D.rotation) || name.startsWith(VisualObject3D.rotation) ||
name.startsWith(PropertyNames3D.scale) || name.startsWith(VisualObject3D.scale) ||
name.toString() == "visible" name == VisualObject3D.visibleKey
) { ) {
//update position of mesh using this object //update position of mesh using this object
mesh.updatePosition(obj) mesh.updatePosition(obj)
} else { } else {
//full update //full update
mesh.geometry = geometryBuilder(obj) mesh.geometry = geometryBuilder(obj)
mesh.material = obj.material mesh.material = obj.material.jsMaterial()
} }
} }
return mesh return mesh
@ -83,17 +86,17 @@ interface ThreeFactory<T : VisualObject> {
/** /**
* Update position, rotation and visibility * 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) position.set(obj.x, obj.y, obj.z)
setRotationFromEuler(obj.euler) setRotationFromEuler(obj.euler)
scale.set(obj.scaleX, obj.scaleY, obj.scaleZ) scale.set(obj.scaleX, obj.scaleY, obj.scaleZ)
visible = obj.visible visible = obj.visible ?: true
} }
/** /**
* Unsafe invocation of a factory * 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)) { if (type.isInstance(obj)) {
return invoke(obj as T) return invoke(obj as T)
} else { } else {
@ -104,8 +107,7 @@ operator fun <T : VisualObject> ThreeFactory<T>.invoke(obj: Any): Object3D {
/** /**
* Basic geometry-based factory * Basic geometry-based factory
*/ */
abstract class MeshThreeFactory<T : VisualObject>(override val type: KClass<out T>) : abstract class MeshThreeFactory<T : VisualObject3D>(override val type: KClass<out T>) : ThreeFactory<T> {
ThreeFactory<T> {
/** /**
* Build a geometry for an object * 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 { override fun invoke(obj: T): Mesh {
//create mesh from geometry //create mesh from geometry
val mesh = buildMesh(obj, ::buildGeometry) return buildMesh<T>(obj) { buildGeometry(it) }
return mesh
} }
} }

View File

@ -4,8 +4,8 @@ import hep.dataforge.context.Context
import hep.dataforge.meta.* import hep.dataforge.meta.*
import hep.dataforge.output.Output import hep.dataforge.output.Output
import hep.dataforge.vis.common.Colors import hep.dataforge.vis.common.Colors
import hep.dataforge.vis.common.VisualObject
import hep.dataforge.vis.hmr.require import hep.dataforge.vis.hmr.require
import hep.dataforge.vis.spatial.VisualObject3D
import info.laht.threekt.WebGLRenderer import info.laht.threekt.WebGLRenderer
import info.laht.threekt.helpers.AxesHelper import info.laht.threekt.helpers.AxesHelper
import info.laht.threekt.lights.AmbientLight import info.laht.threekt.lights.AmbientLight
@ -15,7 +15,7 @@ import kotlin.browser.window
private val elementResizeEvent = require("element-resize-event") 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 override val context: Context get() = three.context
@ -58,7 +58,7 @@ class ThreeOutput(val three: ThreePlugin, val meta: Meta = EmptyMeta) : Output<V
animate() animate()
} }
override fun render(obj: VisualObject, meta: Meta) { override fun render(obj: VisualObject3D, meta: Meta) {
scene.add(three.buildObject3D(obj)) scene.add(three.buildObject3D(obj))
} }
} }

View File

@ -5,8 +5,6 @@ import hep.dataforge.context.PluginFactory
import hep.dataforge.context.PluginTag import hep.dataforge.context.PluginTag
import hep.dataforge.context.content import hep.dataforge.context.content
import hep.dataforge.meta.* import hep.dataforge.meta.*
import hep.dataforge.vis.common.VisualGroup
import hep.dataforge.vis.common.VisualObject
import hep.dataforge.vis.spatial.* import hep.dataforge.vis.spatial.*
import info.laht.threekt.cameras.Camera import info.laht.threekt.cameras.Camera
import info.laht.threekt.cameras.PerspectiveCamera import info.laht.threekt.cameras.PerspectiveCamera
@ -20,7 +18,7 @@ import kotlin.reflect.KClass
class ThreePlugin : AbstractPlugin() { class ThreePlugin : AbstractPlugin() {
override val tag: PluginTag get() = Companion.tag 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) private val compositeFactory = ThreeCompositeFactory(this)
init { init {
@ -31,15 +29,16 @@ class ThreePlugin : AbstractPlugin() {
objectFactories[Cylinder::class] = ThreeCylinderFactory objectFactories[Cylinder::class] = ThreeCylinderFactory
} }
private fun findObjectFactory(type: KClass<out VisualObject>): ThreeFactory<*>? { private fun findObjectFactory(type: KClass<out VisualObject3D>): ThreeFactory<*>? {
return objectFactories[type] return objectFactories[type]
?: context.content<ThreeFactory<*>>(ThreeFactory.TYPE).values.find { it.type == type } ?: context.content<ThreeFactory<*>>(ThreeFactory.TYPE).values.find { it.type == type }
} }
fun buildObject3D(obj: VisualObject): Object3D { fun buildObject3D(obj: VisualObject3D): Object3D {
return when (obj) { return when (obj) {
is VisualGroup -> Group(obj.mapNotNull { is VisualGroup3D -> Group(obj.mapNotNull {
try { try {
it as VisualObject3D
buildObject3D(it) buildObject3D(it)
} catch (ex: Throwable) { } catch (ex: Throwable) {
console.error(ex) console.error(ex)

View File

@ -4,11 +4,7 @@ import hep.dataforge.meta.MetaItem
import hep.dataforge.meta.float import hep.dataforge.meta.float
import hep.dataforge.meta.get import hep.dataforge.meta.get
import hep.dataforge.meta.node import hep.dataforge.meta.node
import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.spatial.*
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 info.laht.threekt.core.BufferGeometry import info.laht.threekt.core.BufferGeometry
import info.laht.threekt.core.Geometry import info.laht.threekt.core.Geometry
import info.laht.threekt.core.Object3D 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) } 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) val MetaItem<*>.vector get() = Vector3(node["x"].float ?: 0f, node["y"].float ?: 0f, node["z"].float ?: 0f)

View File

@ -2,14 +2,12 @@ package hep.dataforge.vis.spatial
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.vis.common.VisualGroup import hep.dataforge.vis.common.VisualGroup
import hep.dataforge.vis.common.VisualLeaf
import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.common.VisualObject
class Box(parent: VisualObject?, val xSize: Number, val ySize: Number, val zSize: Number, meta: Array<out Meta>) : 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 //TODO add helper for color configuration
override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) { override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) {
val dx = xSize.toDouble() / 2 val dx = xSize.toDouble() / 2
val dy = ySize.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 = {}) = //fun VisualGroup.box(meta: Meta = EmptyMeta, action: Box.() -> Unit = {}) =
// Box(this, meta).apply(action).also { add(it) } // Box(this, meta).apply(action).also { add(it) }
fun VisualGroup.box( inline fun VisualGroup.box(
xSize: Number, xSize: Number,
ySize: Number, ySize: Number,
zSize: Number, zSize: Number,
name: String? = null, name: String? = null,
vararg meta: Meta, vararg meta: Meta,
action: Box.() -> Unit = {} 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) }

View File

@ -1,10 +1,9 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.meta.seal import hep.dataforge.meta.isEmpty
import hep.dataforge.meta.update import hep.dataforge.meta.update
import hep.dataforge.vis.common.VisualGroup import hep.dataforge.vis.common.VisualGroup
import hep.dataforge.vis.common.VisualLeaf
import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.common.VisualObject
enum class CompositeType { enum class CompositeType {
@ -15,33 +14,38 @@ enum class CompositeType {
open class Composite( open class Composite(
parent: VisualObject?, parent: VisualObject?,
val first: VisualObject, val first: VisualObject3D,
val second: VisualObject, val second: VisualObject3D,
val type: CompositeType = CompositeType.UNION, val type: CompositeType = CompositeType.UNION,
meta: Array<out Meta> meta: Array<out Meta>
) : VisualLeaf(parent, meta) ) : VisualLeaf3D(parent, meta)
fun VisualGroup.composite( fun VisualGroup.composite(
type: CompositeType, type: CompositeType,
name: String? = null, name: String? = null,
vararg meta: Meta, vararg meta: Meta,
builder: VisualGroup.() -> Unit builder: VisualGroup3D.() -> Unit
): Composite { ): Composite {
val group = VisualGroup().apply(builder) val group = VisualGroup3D().apply(builder)
val children = group.toList() val children = group.filterIsInstance<VisualObject3D>()
if (children.size != 2) error("Composite requires exactly two children") 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 { 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) 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) 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) 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) composite(CompositeType.INTERSECT, name, *meta, builder = builder)

View File

@ -2,10 +2,9 @@ package hep.dataforge.vis.spatial
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.vis.common.VisualGroup import hep.dataforge.vis.common.VisualGroup
import hep.dataforge.vis.common.VisualLeaf
import hep.dataforge.vis.common.VisualObject 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 { companion object {

View File

@ -2,7 +2,6 @@ package hep.dataforge.vis.spatial
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.vis.common.VisualGroup import hep.dataforge.vis.common.VisualGroup
import hep.dataforge.vis.common.VisualLeaf
import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.common.VisualObject
import hep.dataforge.vis.common.number import hep.dataforge.vis.common.number
import kotlin.math.PI import kotlin.math.PI
@ -10,11 +9,9 @@ import kotlin.math.PI
/** /**
* A cylinder or cut cone segment * A cylinder or cut cone segment
*/ */
class Cylinder(parent: VisualObject?, radius: Number, height: Number, meta: Array<out Meta>) : class Cylinder(parent: VisualObject?, var radius: Number, var height: Number, meta: Array<out Meta>) :
VisualLeaf(parent, meta) { VisualLeaf3D(parent, meta) {
var radius by number(radius)
var upperRadius by number(radius) var upperRadius by number(radius)
var height by number(height)
var startAngle by number(0.0) var startAngle by number(0.0)
var angle by number(2 * PI) var angle by number(2 * PI)
} }

View File

@ -2,7 +2,6 @@ package hep.dataforge.vis.spatial
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.vis.common.VisualGroup import hep.dataforge.vis.common.VisualGroup
import hep.dataforge.vis.common.VisualLeaf
import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.common.VisualObject
import kotlin.math.PI import kotlin.math.PI
import kotlin.math.cos 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) 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() var shape: List<Point2D> = ArrayList()

View File

@ -1,7 +1,6 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.meta.* import hep.dataforge.meta.*
import hep.dataforge.vis.common.VisualObject
data class Point2D(val x: Number, val y: Number) : MetaRepr { data class Point2D(val x: Number, val y: Number) : MetaRepr {
override fun toMeta(): Meta = buildMeta { override fun toMeta(): Meta = buildMeta {
@ -60,6 +59,6 @@ fun GeometryBuilder<*>.face4(
face(vertex1, vertex3, vertex4, normal, meta) face(vertex1, vertex3, vertex4, normal, meta)
} }
interface Shape : VisualObject { interface Shape : VisualObject3D {
fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>)
} }

View File

@ -2,17 +2,15 @@ package hep.dataforge.vis.spatial
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.vis.common.VisualGroup import hep.dataforge.vis.common.VisualGroup
import hep.dataforge.vis.common.VisualLeaf
import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.common.VisualObject
import hep.dataforge.vis.common.double import hep.dataforge.vis.common.number
import kotlin.math.PI import kotlin.math.PI
class Sphere(parent: VisualObject?, meta: Array<out Meta>) : VisualLeaf(parent, meta) { class Sphere(parent: VisualObject?, var radius: Number, meta: Array<out Meta>) : VisualLeaf3D(parent, meta) {
var radius by double(50.0) var phiStart by number(0.0)
var phiStart by double(0.0) var phi by number(2 * PI)
var phi by double(2 * PI) var thetaStart by number(0.0)
var thetaStart by double(0.0) var theta by number(PI)
var theta by double(PI)
} }
fun VisualGroup.sphere( fun VisualGroup.sphere(
@ -22,8 +20,7 @@ fun VisualGroup.sphere(
name: String? = null, name: String? = null,
vararg meta: Meta, vararg meta: Meta,
action: Sphere.() -> Unit = {} action: Sphere.() -> Unit = {}
) = Sphere(this, meta).apply(action).apply { ) = Sphere(this, radius, meta).apply(action).apply {
this.radius = radius.toDouble()
this.phi = phi.toDouble() this.phi = phi.toDouble()
this.theta = theta.toDouble() this.theta = theta.toDouble()
}.also { set(name, it) } }.also { set(name, it) }

View File

@ -1,22 +1,32 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.meta.* import hep.dataforge.meta.*
import hep.dataforge.names.Name
import hep.dataforge.names.plus import hep.dataforge.names.plus
import hep.dataforge.output.Output import hep.dataforge.output.Output
import hep.dataforge.vis.common.VisualGroup import hep.dataforge.vis.common.VisualGroup
import hep.dataforge.vis.common.VisualLeaf
import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.common.VisualObject
import hep.dataforge.vis.common.asName 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 = data class Value3(var x: Float = 0f, var y: Float = 0f, var z: Float = 0f)
VisualGroup(this, meta).apply(action).also { set(key, it) }
interface VisualObject3D : VisualObject {
var position: Value3
var rotation: Value3
var scale: Value3
fun Output<VisualObject>.render(meta: Meta = EmptyMeta, action: VisualGroup.() -> Unit) = fun setProperty(name: Name, value: Any?)
render(VisualGroup().apply(action), meta) 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 x = "x".asName()
val y = "y".asName() val y = "y".asName()
val z = "z".asName() val z = "z".asName()
@ -41,78 +51,65 @@ object PropertyNames3D {
val yScale = scale + y val yScale = scale + y
val zScale = scale + z 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 // 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 { enum class RotationOrder {
XYZ, XYZ,
@ -124,52 +121,40 @@ enum class RotationOrder {
} }
/** /**
* Rotation order. Not inherited * Rotation order
*/ */
var VisualObject.rotationOrder: RotationOrder var VisualObject3D.rotationOrder: RotationOrder
get() = config[PropertyNames3D.rotationOrder].enum<RotationOrder>() ?: RotationOrder.XYZ get() = getProperty(VisualObject3D.rotationOrder).enum<RotationOrder>() ?: RotationOrder.XYZ
set(value) { set(value) = setProperty(VisualObject3D.rotationOrder, value)
config[PropertyNames3D.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 * Preferred number of polygons for displaying the object. If not defined, uses shape or renderer default
*/ */
var VisualObject.detail: Int? var VisualObject3D.detail: Int?
get() = properties["detail"]?.int get() = getProperty(detailKey).int
set(value) { set(value) = setProperty(detailKey, value)
config["detail"] = 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 { object World {

View File

@ -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()}

View File

@ -18,7 +18,7 @@ class GroupTest {
rotationY = PI / 4 rotationY = PI / 4
} }
box(100, 100, 100) box(100, 100, 100)
color { material {
"color" to Colors.lightgreen "color" to Colors.lightgreen
"opacity" to 0.3 "opacity" to 0.3
} }
@ -31,7 +31,7 @@ class GroupTest {
} }
box(100, 100, 100) box(100, 100, 100)
y = 300 y = 300
color(Colors.red) material(Colors.red)
} }
subtract{ subtract{
box(100, 100, 100) { box(100, 100, 100) {
@ -41,7 +41,7 @@ class GroupTest {
} }
box(100, 100, 100) box(100, 100, 100)
y = -300 y = -300
color(Colors.blue) material(Colors.blue)
} }
} }