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
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
*/
@ -181,23 +175,3 @@ object Colors {
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
}

View File

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

View File

@ -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 {

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 {

View File

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

View File

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

View File

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

View File

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

View File

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

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