GDML demo renders something

This commit is contained in:
Alexander Nozik 2019-07-27 20:05:44 +03:00
parent 687393c243
commit d3500c3a57
23 changed files with 553 additions and 438 deletions

View File

@ -4,6 +4,7 @@ import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaBuilder import hep.dataforge.meta.MetaBuilder
import hep.dataforge.meta.buildMeta import hep.dataforge.meta.buildMeta
import hep.dataforge.meta.set 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,13 +182,14 @@ object Colors {
const val yellowgreen = 0x9ACD32 const val yellowgreen = 0x9ACD32
} }
private val material = "material".toName()
fun VisualObject.color(rgb: Int) { fun VisualObject.color(rgb: Int) {
this.properties["material"] = rgb this.config[material] = rgb
} }
fun VisualObject.color(meta: Meta) { fun VisualObject.color(meta: Meta) {
this.properties["material"] = meta this.config[material] = meta
} }
fun VisualObject.color(builder: MetaBuilder.() -> Unit) { fun VisualObject.color(builder: MetaBuilder.() -> Unit) {

View File

@ -1,29 +1,41 @@
package hep.dataforge.vis.common package hep.dataforge.vis.common
import hep.dataforge.meta.EmptyMeta import hep.dataforge.meta.Config
import hep.dataforge.meta.Laminate
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.meta.Styled
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.toName
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
/** /**
* A display group which allows both named and unnamed children * A display group which allows both named and unnamed children
*/ */
class VisualGroup( class VisualGroup(
override val parent: VisualObject? = null, meta: Meta = EmptyMeta override val parent: VisualObject? = null, tagRefs: Array<out Meta> = emptyArray()
) : VisualObject, Iterable<VisualObject>, Provider { ) : VisualObject, Iterable<VisualObject>, Provider {
private val namedChildren = HashMap<Name, VisualObject>() private val namedChildren = HashMap<Name, VisualObject>()
private val unnamedChildren = ArrayList<VisualObject>() private val unnamedChildren = ArrayList<VisualObject>()
override val defaultTarget: String get() = VisualObject.TYPE override val defaultTarget: String get() = VisualObject.TYPE
override val properties: Styled = Styled(meta) override val config = Config()
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()
override fun provideTop(target: String): Map<Name, Any> { override fun provideTop(target: String): Map<Name, Any> {
return when(target){ return when (target) {
VisualObject.TYPE -> namedChildren VisualObject.TYPE -> namedChildren
else -> emptyMap() else -> emptyMap()
} }
@ -49,13 +61,12 @@ class VisualGroup(
} }
/** /**
* * Add named or unnamed child to the group. If key is [null] the child is considered unnamed. Both key and value are not
* allowed to be null in the same time. If name is present and [child] is null, the appropriate element is removed.
*/ */
operator fun set(key: String?, child: VisualObject?) { operator fun set(name: Name?, child: VisualObject?) {
if(key == null){ when {
name != null -> {
} else {
val name = key.toName()
if (child == null) { if (child == null) {
namedChildren.remove(name) namedChildren.remove(name)
} else { } else {
@ -63,7 +74,12 @@ class VisualGroup(
} }
listeners.forEach { it.callback(name, child) } listeners.forEach { it.callback(name, child) }
} }
child != null -> unnamedChildren.add(child)
else -> error("Both key and child element are empty")
} }
}
operator fun set(key: String?, child: VisualObject?) = set(key?.asName(), child)
/** /**
* Append unnamed child * Append unnamed child

View File

@ -2,24 +2,34 @@ package hep.dataforge.vis.common
import hep.dataforge.meta.* import hep.dataforge.meta.*
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.toName
import hep.dataforge.provider.Type import hep.dataforge.provider.Type
import hep.dataforge.vis.common.VisualObject.Companion.META_KEY import hep.dataforge.vis.common.VisualObject.Companion.META_KEY
import hep.dataforge.vis.common.VisualObject.Companion.TAGS_KEY import hep.dataforge.vis.common.VisualObject.Companion.TAGS_KEY
import hep.dataforge.vis.common.VisualObject.Companion.TYPE import hep.dataforge.vis.common.VisualObject.Companion.TYPE
private fun Laminate.withTop(meta: Meta): Laminate = Laminate(listOf(meta) + layers)
private fun Laminate.withBottom(meta: Meta): Laminate = Laminate(layers + meta)
/** /**
* A root type for display hierarchy * A root type for display hierarchy
*/ */
@Type(TYPE) @Type(TYPE)
interface VisualObject : MetaRepr { interface VisualObject : MetaRepr, Configurable {
/** /**
* The parent object of this one. If null, this one is a root. * The parent object of this one. If null, this one is a root.
*/ */
val parent: VisualObject? val parent: VisualObject?
val properties: Styled /**
* Individual properties configurator
*/
override val config: Config
/**
* All properties including inherited ones
*/
val properties: Laminate
override fun toMeta(): Meta = buildMeta { override fun toMeta(): Meta = buildMeta {
"type" to this::class "type" to this::class
@ -37,18 +47,11 @@ interface VisualObject : MetaRepr {
} }
} }
/**
* Get the property of this display object of parent's if not found
*/
tailrec fun VisualObject.getProperty(name: Name): MetaItem<*>? = properties[name] ?: parent?.getProperty(name)
fun VisualObject.getProperty(name: String): MetaItem<*>? = getProperty(name.toName())
/** /**
* A change listener for [VisualObject] configuration. * A change listener for [VisualObject] configuration.
*/ */
fun VisualObject.onChange(owner: Any?, action: (Name, before: MetaItem<*>?, after: MetaItem<*>?) -> Unit) { fun VisualObject.onChange(owner: Any?, action: (Name, before: MetaItem<*>?, after: MetaItem<*>?) -> Unit) {
properties.onChange(owner, action) config.onChange(owner, action)
parent?.onChange(owner, action) parent?.onChange(owner, action)
} }
@ -56,7 +59,7 @@ fun VisualObject.onChange(owner: Any?, action: (Name, before: MetaItem<*>?, afte
* Remove all meta listeners with matching owners * Remove all meta listeners with matching owners
*/ */
fun VisualObject.removeChangeListener(owner: Any?) { fun VisualObject.removeChangeListener(owner: Any?) {
properties.removeListener(owner) config.removeListener(owner)
parent?.removeChangeListener(owner) parent?.removeChangeListener(owner)
} }
@ -64,18 +67,28 @@ fun VisualObject.removeChangeListener(owner: Any?) {
/** /**
* Additional meta not relevant to display * Additional meta not relevant to display
*/ */
val VisualObject.meta: Meta get() = properties[META_KEY]?.node ?: EmptyMeta val VisualObject.meta: Meta get() = config[META_KEY]?.node ?: EmptyMeta
val VisualObject.tags: List<String> get() = properties[TAGS_KEY].stringList val VisualObject.tags: List<String> get() = config[TAGS_KEY].stringList
/** /**
* Basic [VisualObject] leaf element * Basic [VisualObject] leaf element
*/ */
open class DisplayLeaf( open class VisualLeaf(
override val parent: VisualObject?, final override val parent: VisualObject?,
meta: Meta = EmptyMeta tagRefs: Array<out Meta>
) : VisualObject { ) : VisualObject {
final override val properties = Styled(meta) final override val config = Config()
override val properties: Laminate by lazy { combineProperties(parent, config, tagRefs) }
}
internal fun combineProperties(parent: VisualObject?, config: Config, tagRefs: Array<out Meta>): Laminate {
val list = ArrayList<Meta>(tagRefs.size + 2)
list += config
list.addAll(tagRefs)
parent?.properties?.let { list.add(it) }
return Laminate(list)
} }
///** ///**

View File

@ -2,13 +2,16 @@ package hep.dataforge.vis.common
import hep.dataforge.meta.* import hep.dataforge.meta.*
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.toName import hep.dataforge.names.NameToken
import hep.dataforge.names.asName
import hep.dataforge.values.Value import hep.dataforge.values.Value
import kotlin.jvm.JvmName import kotlin.jvm.JvmName
import kotlin.properties.ReadOnlyProperty import kotlin.properties.ReadOnlyProperty
import kotlin.properties.ReadWriteProperty import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
fun String.asName() = NameToken(this).asName()
/** /**
* A delegate for display object properties * A delegate for display object properties
*/ */
@ -18,17 +21,17 @@ class DisplayObjectDelegate(
val inherited: Boolean val inherited: Boolean
) : ReadWriteProperty<VisualObject, MetaItem<*>?> { ) : ReadWriteProperty<VisualObject, MetaItem<*>?> {
override fun getValue(thisRef: VisualObject, property: KProperty<*>): MetaItem<*>? { override fun getValue(thisRef: VisualObject, property: KProperty<*>): MetaItem<*>? {
val name = key ?: property.name.toName() val name = key ?: property.name.asName()
return if (inherited) { return if (inherited) {
thisRef.getProperty(name)
} else {
thisRef.properties[name] thisRef.properties[name]
} else {
thisRef.config[name]
} ?: default } ?: default
} }
override fun setValue(thisRef: VisualObject, property: KProperty<*>, value: MetaItem<*>?) { override fun setValue(thisRef: VisualObject, property: KProperty<*>, value: MetaItem<*>?) {
val name = key ?: property.name.toName() val name = key ?: property.name.asName()
thisRef.properties[name] = value thisRef.config[name] = value
} }
} }
@ -43,70 +46,71 @@ class DisplayObjectDelegateWrapper<T>(
//private var cachedName: Name? = null //private var cachedName: Name? = null
override fun getValue(thisRef: VisualObject, property: KProperty<*>): T { override fun getValue(thisRef: VisualObject, property: KProperty<*>): T {
val name = key ?: property.name.toName() val name = key ?: property.name.asName()
return if (inherited) { return if (inherited) {
read(thisRef.getProperty(name))
} else {
read(thisRef.properties[name]) read(thisRef.properties[name])
} else {
read(thisRef.config[name])
} ?: default } ?: default
} }
override fun setValue(thisRef: VisualObject, property: KProperty<*>, value: T) { override fun setValue(thisRef: VisualObject, property: KProperty<*>, value: T) {
val name = key ?: property.name.toName() val name = key ?: property.name.asName()
thisRef.properties[name] = value thisRef.config[name] = value
} }
} }
fun VisualObject.value(default: Value? = null, key: String? = null, inherited: Boolean = false) = fun VisualObject.value(default: Value? = null, key: String? = null, inherited: Boolean = false) =
DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.value } DisplayObjectDelegateWrapper(key?.asName(), default, inherited) { it.value }
fun VisualObject.string(default: String? = null, key: String? = null, inherited: Boolean = false) = fun VisualObject.string(default: String? = null, key: String? = null, inherited: Boolean = false) =
DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.string } DisplayObjectDelegateWrapper(key?.asName(), default, inherited) { it.string }
fun VisualObject.boolean(default: Boolean? = null, key: String? = null, inherited: Boolean = false) = fun VisualObject.boolean(default: Boolean? = null, key: String? = null, inherited: Boolean = false) =
DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.boolean } DisplayObjectDelegateWrapper(key?.asName(), default, inherited) { it.boolean }
fun VisualObject.number(default: Number? = null, key: String? = null, inherited: Boolean = false) = fun VisualObject.number(default: Number? = null, key: String? = null, inherited: Boolean = false) =
DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.number } DisplayObjectDelegateWrapper(key?.asName(), default, inherited) { it.number }
fun VisualObject.double(default: Double? = null, key: String? = null, inherited: Boolean = false) = fun VisualObject.double(default: Double? = null, key: String? = null, inherited: Boolean = false) =
DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.double } DisplayObjectDelegateWrapper(key?.asName(), default, inherited) { it.double }
fun VisualObject.int(default: Int? = null, key: String? = null, inherited: Boolean = false) = fun VisualObject.int(default: Int? = null, key: String? = null, inherited: Boolean = false) =
DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.int } DisplayObjectDelegateWrapper(key?.asName(), default, inherited) { it.int }
fun VisualObject.node(key: String? = null, inherited: Boolean = true) = fun VisualObject.node(key: String? = null, inherited: Boolean = true) =
DisplayObjectDelegateWrapper(key?.toName(), null, inherited) { it.node } DisplayObjectDelegateWrapper(key?.asName(), null, inherited) { it.node }
fun VisualObject.item(key: String? = null, inherited: Boolean = true) = fun VisualObject.item(key: String? = null, inherited: Boolean = true) =
DisplayObjectDelegateWrapper(key?.toName(), null, inherited) { it } DisplayObjectDelegateWrapper(key?.asName(), null, inherited) { it }
//fun <T : Configurable> Configurable.spec(spec: Specification<T>, key: String? = null) = ChildConfigDelegate<T>(key) { spec.wrap(this) } //fun <T : Configurable> Configurable.spec(spec: Specification<T>, key: String? = null) = ChildConfigDelegate<T>(key) { spec.wrap(this) }
@JvmName("safeString") @JvmName("safeString")
fun VisualObject.string(default: String, key: String? = null, inherited: Boolean = false) = fun VisualObject.string(default: String, key: String? = null, inherited: Boolean = false) =
DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.string } DisplayObjectDelegateWrapper(key?.asName(), default, inherited) { it.string }
@JvmName("safeBoolean") @JvmName("safeBoolean")
fun VisualObject.boolean(default: Boolean, key: String? = null, inherited: Boolean = false) = fun VisualObject.boolean(default: Boolean, key: String? = null, inherited: Boolean = false) =
DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.boolean } DisplayObjectDelegateWrapper(key?.asName(), default, inherited) { it.boolean }
@JvmName("safeNumber") @JvmName("safeNumber")
fun VisualObject.number(default: Number, key: String? = null, inherited: Boolean = false) = fun VisualObject.number(default: Number, key: String? = null, inherited: Boolean = false) =
DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.number } DisplayObjectDelegateWrapper(key?.asName(), default, inherited) { it.number }
@JvmName("safeDouble") @JvmName("safeDouble")
fun VisualObject.double(default: Double, key: String? = null, inherited: Boolean = false) = fun VisualObject.double(default: Double, key: String? = null, inherited: Boolean = false) =
DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.double } DisplayObjectDelegateWrapper(key?.asName(), default, inherited) { it.double }
@JvmName("safeInt") @JvmName("safeInt")
fun VisualObject.int(default: Int, key: String? = null, inherited: Boolean = false) = fun VisualObject.int(default: Int, key: String? = null, inherited: Boolean = false) =
DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.int } DisplayObjectDelegateWrapper(key?.asName(), default, inherited) { it.int }
inline fun <reified E : Enum<E>> VisualObject.enum(default: E, key: String? = null, inherited: Boolean = false) = inline fun <reified E : Enum<E>> VisualObject.enum(default: E, key: String? = null, inherited: Boolean = false) =
DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { item -> item.string?.let { enumValueOf<E>(it) } } DisplayObjectDelegateWrapper(key?.let{ NameToken(it).asName()}, default, inherited) { item -> item.string?.let { enumValueOf<E>(it) } }
//merge properties //merge properties
@ -116,11 +120,11 @@ fun <T> VisualObject.merge(
): ReadOnlyProperty<VisualObject, T> { ): ReadOnlyProperty<VisualObject, T> {
return object : ReadOnlyProperty<VisualObject, T> { return object : ReadOnlyProperty<VisualObject, T> {
override fun getValue(thisRef: VisualObject, property: KProperty<*>): T { override fun getValue(thisRef: VisualObject, property: KProperty<*>): T {
val name = key?.toName() ?: property.name.toName() val name = key?.asName() ?: property.name.asName()
val sequence = sequence<MetaItem<*>> { val sequence = sequence<MetaItem<*>> {
var thisObj: VisualObject? = thisRef var thisObj: VisualObject? = thisRef
while (thisObj != null) { while (thisObj != null) {
thisObj.properties[name]?.let { yield(it) } thisObj.config[name]?.let { yield(it) }
thisObj = thisObj.parent thisObj = thisObj.parent
} }
} }

View File

@ -4,7 +4,6 @@ import hep.dataforge.meta.*
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.toName import hep.dataforge.names.toName
import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.common.VisualObject
import hep.dataforge.vis.common.getProperty
import hep.dataforge.vis.common.onChange import hep.dataforge.vis.common.onChange
import javafx.beans.binding.ObjectBinding import javafx.beans.binding.ObjectBinding
import tornadofx.* import tornadofx.*
@ -24,7 +23,7 @@ class DisplayObjectFXListener(val obj: VisualObject) {
operator fun get(key: Name): ObjectBinding<MetaItem<*>?> { operator fun get(key: Name): ObjectBinding<MetaItem<*>?> {
return binndings.getOrPut(key) { return binndings.getOrPut(key) {
object : ObjectBinding<MetaItem<*>?>() { object : ObjectBinding<MetaItem<*>?>() {
override fun computeValue(): MetaItem<*>? = obj.getProperty(key) override fun computeValue(): MetaItem<*>? = obj.properties[key]
} }
} }
} }

View File

@ -27,21 +27,14 @@ class RendererDemoView : View() {
renderer.render { renderer.render {
group = group { group = group {
box { box(100,100,100)
xSize = 100.0 box(100,100,100) {
ySize = 100.0
zSize = 100.0
}
box {
x = 110.0 x = 110.0
xSize = 100.0
ySize = 100.0
zSize = 100.0
} }
} }
} }
var color by group.properties.number(1530).int var color by group.config.number(1530).int
GlobalScope.launch { GlobalScope.launch {
val random = Random(111) val random = Random(111)

View File

@ -11,28 +11,57 @@ import kotlin.math.cos
import kotlin.math.sin import kotlin.math.sin
private fun VisualObject.applyPosition(pos: GDMLPosition): VisualObject = apply { private fun VisualObject.withPosition(
pos: GDMLPosition? = null,
rotation: GDMLRotation? = null,
scale: GDMLScale? = null
): VisualObject =
apply {
// if( this is VisualObject3D){
// pos?.let {
// x = pos.x
// y = pos.y
// z = pos.z
// }
// rotation?.let {
// rotationX = rotation.x
// rotationY = rotation.y
// rotationZ = rotation.z
// }
// } else {
pos?.let {
x = pos.x x = pos.x
y = pos.y y = pos.y
z = pos.z z = pos.z
//TODO convert units if needed }
} rotation?.let {
private fun VisualObject.applyRotation(rotation: GDMLRotation): VisualObject = apply {
rotationX = rotation.x rotationX = rotation.x
rotationY = rotation.y rotationY = rotation.y
rotationZ = rotation.z rotationZ = rotation.z
}
//}
scale?.let {
scaleX = scale.x
scaleY = scale.y
scaleZ = scale.z
}
//TODO convert units if needed //TODO convert units if needed
} }
private fun VisualGroup.addSolid(root: GDML, solid: GDMLSolid, block: VisualObject.() -> Unit = {}): VisualObject {
private fun VisualGroup.addSolid(
root: GDML,
solid: GDMLSolid,
name: String? = null,
block: VisualObject.() -> Unit = {}
): VisualObject {
return when (solid) { return when (solid) {
is GDMLBox -> box(solid.x, solid.y, solid.z) is GDMLBox -> box(solid.x, solid.y, solid.z, name)
is GDMLTube -> cylinder(solid.rmax, solid.z) { is GDMLTube -> cylinder(solid.rmax, solid.z, name) {
startAngle = solid.startphi startAngle = solid.startphi
angle = solid.deltaphi angle = solid.deltaphi
} }
is GDMLXtru -> extrude { is GDMLXtru -> extrude(name) {
shape { shape {
solid.vertices.forEach { solid.vertices.forEach {
point(it.x, it.y) point(it.x, it.y)
@ -46,6 +75,7 @@ private fun VisualGroup.addSolid(root: GDML, solid: GDMLSolid, block: VisualObje
//Add solid with modified scale //Add solid with modified scale
val innerSolid = solid.solidref.resolve(root) val innerSolid = solid.solidref.resolve(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) { addSolid(root, innerSolid) {
block() block()
scaleX = scaleX.toDouble() * solid.scale.x.toDouble() scaleX = scaleX.toDouble() * solid.scale.x.toDouble()
@ -53,12 +83,12 @@ private fun VisualGroup.addSolid(root: GDML, solid: GDMLSolid, block: VisualObje
scaleZ = scaleZ.toDouble() * solid.scale.z.toDouble() scaleZ = scaleZ.toDouble() * solid.scale.z.toDouble()
} }
} }
is GDMLSphere -> sphere(solid.rmax, solid.deltaphi, solid.deltatheta) { is GDMLSphere -> sphere(solid.rmax, solid.deltaphi, solid.deltatheta, name) {
phiStart = solid.startphi.toDouble() phiStart = solid.startphi.toDouble()
thetaStart = solid.starttheta.toDouble() thetaStart = solid.starttheta.toDouble()
} }
is GDMLOrb -> sphere(solid.r) is GDMLOrb -> sphere(solid.r, name = name)
is GDMLPolyhedra -> extrude { is GDMLPolyhedra -> extrude(name) {
//getting the radius of first //getting the radius of first
require(solid.planes.size > 1) { "The polyhedron geometry requires at least two planes" } require(solid.planes.size > 1) { "The polyhedron geometry requires at least two planes" }
val baseRadius = solid.planes.first().rmax.toDouble() val baseRadius = solid.planes.first().rmax.toDouble()
@ -81,14 +111,13 @@ private fun VisualGroup.addSolid(root: GDML, solid: GDMLSolid, block: VisualObje
is GDMLSubtraction -> CompositeType.SUBTRACT is GDMLSubtraction -> CompositeType.SUBTRACT
is GDMLIntersection -> CompositeType.INTERSECT is GDMLIntersection -> CompositeType.INTERSECT
} }
return composite(type) {
return composite(type, name) {
addSolid(root, first) { addSolid(root, first) {
solid.resolveFirstPosition(root)?.let { applyPosition(it) } withPosition(solid.resolveFirstPosition(root), solid.resolveFirstRotation(root), null)
solid.resolveFirstRotation(root)?.let { applyRotation(it) }
} }
addSolid(root, second) addSolid(root, second)
solid.resolvePosition(root)?.let { applyPosition(it) } withPosition(solid.resolvePosition(root), solid.resolveRotation(root), null)
solid.resolveRotation(root)?.let { applyRotation(it) }
} }
} }
}.apply(block) }.apply(block)
@ -97,30 +126,42 @@ private fun VisualGroup.addSolid(root: GDML, solid: GDMLSolid, block: VisualObje
private fun VisualGroup.addVolume( private fun VisualGroup.addVolume(
root: GDML, root: GDML,
group: GDMLGroup, group: GDMLGroup,
position: GDMLPosition? = null,
rotation: GDMLRotation? = null,
scale: GDMLScale? = null,
resolveColor: GDMLMaterial.() -> Meta resolveColor: GDMLMaterial.() -> Meta
): VisualGroup { ) {
group(group.name) {
withPosition(position, rotation, scale)
if (group is GDMLVolume) { if (group is GDMLVolume) {
val solid = group.solidref.resolve(root) val solid = group.solidref.resolve(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) val material = group.materialref.resolve(root)
?: error("Material with tag ${group.materialref.ref} for volume ${group.name} not defined") ?: error("Material with tag ${group.materialref.ref} for volume ${group.name} not defined")
addSolid(root, solid) { addSolid(root, solid, solid.name) {
color(material.resolveColor()) color(material.resolveColor())
} }
//TODO render placements //TODO render placements
} }
group.physVolumes.forEach { group.physVolumes.forEach { physVolume ->
val volume: GDMLGroup = val volume: GDMLGroup = physVolume.volumeref.resolve(root)
it.volumeref.resolve(root) ?: error("Volume with ref ${it.volumeref.ref} could not be resolved") ?: error("Volume with ref ${physVolume.volumeref.ref} could not be resolved")
addVolume(root, volume, resolveColor).apply {
it.resolvePosition(root)?.let { pos -> applyPosition(pos) } addVolume(
it.resolveRotation(root)?.let { rot -> applyRotation(rot) } root,
volume,
physVolume.resolvePosition(root),
physVolume.resolveRotation(root),
physVolume.resolveScale(root),
resolveColor
)
} }
} }
return this
} }

View File

@ -17,7 +17,7 @@ import scientifik.gdml.GDML
import kotlin.browser.document import kotlin.browser.document
import kotlin.dom.clear import kotlin.dom.clear
class GDMLDemoApp : ApplicationBase() { private class GDMLDemoApp : ApplicationBase() {
/** /**
@ -56,13 +56,13 @@ class GDMLDemoApp : ApplicationBase() {
val context = Global.context("demo") {} val context = Global.context("demo") {}
val three = context.plugins.load(ThreePlugin) val three = context.plugins.load(ThreePlugin)
val canvas = document.getElementById("canvas") ?: error("Element with id canvas not found on page")
canvas.clear()
val output = three.output(canvas)
//val url = URL("https://drive.google.com/open?id=1w5e7fILMN83JGgB8WANJUYm8OW2s0WVO") //val url = URL("https://drive.google.com/open?id=1w5e7fILMN83JGgB8WANJUYm8OW2s0WVO")
val canvas = document.getElementById("canvas") ?: error("Element with id canvas not found on page")
val action: suspend (String) -> Unit = { val action: suspend (String) -> Unit = {
canvas.clear()
val output = three.output(canvas)
val gdml = GDML.format.parse(GDML.serializer(), it) val gdml = GDML.format.parse(GDML.serializer(), it)
val visual = gdml.toVisual() val visual = gdml.toVisual()
output.render(visual) output.render(visual)

View File

@ -13,7 +13,7 @@ class BMNTest {
val url = URL("https://drive.google.com/open?id=1w5e7fILMN83JGgB8WANJUYm8OW2s0WVO") 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 file = File("D:\\Work\\Projects\\gdml.kt\\src\\commonTest\\resources\\gdml\\geofile_full.xml")
val stream = if(file.exists()){ val stream = if (file.exists()) {
file.inputStream() file.inputStream()
} else { } else {
url.openStream() url.openStream()
@ -21,7 +21,7 @@ class BMNTest {
val xmlReader = StAXReader(stream, "UTF-8") val xmlReader = StAXReader(stream, "UTF-8")
val xml = GDML.format.parse(GDML.serializer(), xmlReader) val xml = GDML.format.parse(GDML.serializer(), xmlReader)
repeat(5) { repeat(20) {
xml.toVisual() xml.toVisual()
} }
} }

View File

@ -17,7 +17,7 @@ import kotlin.math.sin
import kotlin.random.Random import kotlin.random.Random
class ThreeDemoApp : ApplicationBase() { private class ThreeDemoApp : ApplicationBase() {
override val stateKeys: List<String> = emptyList() override val stateKeys: List<String> = emptyList()
@ -39,18 +39,12 @@ class ThreeDemoApp : ApplicationBase() {
demo("dynamic", "Dynamic properties") { demo("dynamic", "Dynamic properties") {
val group = group { val group = group {
box { box(100, 100, 100) {
z = 110.0 z = 110.0
xSize = 100.0
ySize = 100.0
zSize = 100.0
} }
box { box(100, 100, 100) {
visible = false visible = false
x = 110.0 x = 110.0
xSize = 100.0
ySize = 100.0
zSize = 100.0
//override color for this cube //override color for this cube
color(1530) color(1530)
@ -63,7 +57,7 @@ class ThreeDemoApp : ApplicationBase() {
} }
} }
var material by group.properties.number(1530).int var material by group.config.number(1530).int
GlobalScope.launch { GlobalScope.launch {
val random = Random(111) val random = Random(111)
@ -100,34 +94,28 @@ class ThreeDemoApp : ApplicationBase() {
demo("CSG", "CSG operations") { demo("CSG", "CSG operations") {
composite(CompositeType.UNION) { composite(CompositeType.UNION) {
box(100, 100, 100) { box(100, 100, 100) {
z = 100 z = 50
rotationX = PI / 4
rotationY = PI / 4
} }
box(100, 100, 100) sphere(50)
color { color {
"color" to Colors.lightgreen "color" to Colors.lightgreen
"opacity" to 0.3 "opacity" to 0.3
} }
} }
composite(CompositeType.INTERSECT) { composite(CompositeType.INTERSECT) {
box(100, 100, 100) {
z = 100
rotationX = PI / 4
rotationY = PI / 4
}
box(100, 100, 100)
y = 300 y = 300
box(100, 100, 100) {
z = 50
}
sphere(50)
color(Colors.red) color(Colors.red)
} }
composite(CompositeType.SUBTRACT) { composite(CompositeType.SUBTRACT) {
box(100, 100, 100) {
z = 100
rotationX = PI / 4
rotationY = PI / 4
}
box(100, 100, 100)
y = -300 y = -300
box(100, 100, 100) {
z = 50
}
sphere(50)
color(Colors.blue) color(Colors.blue)
} }
} }

View File

@ -11,9 +11,9 @@ object ThreeCylinderFactory : MeshThreeFactory<Cylinder>(Cylinder::class) {
return obj.detail?.let { return 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,
radiusBottom = obj.radius!!, radiusBottom = obj.radius,
height = obj.height!!, height = obj.height,
radialSegments = segments, radialSegments = segments,
heightSegments = segments, heightSegments = segments,
openEnded = false, openEnded = false,
@ -21,9 +21,9 @@ object ThreeCylinderFactory : MeshThreeFactory<Cylinder>(Cylinder::class) {
thetaLength = obj.angle thetaLength = obj.angle
) )
} ?: CylinderBufferGeometry( } ?: CylinderBufferGeometry(
radiusTop = obj.upperRadius!!, radiusTop = obj.upperRadius,
radiusBottom = obj.radius!!, radiusBottom = obj.radius,
height = obj.height!!, height = obj.height,
openEnded = false, openEnded = false,
thetaStart = obj.startAngle, thetaStart = obj.startAngle,
thetaLength = obj.angle thetaLength = obj.angle

View File

@ -1,11 +1,10 @@
package hep.dataforge.vis.spatial.three package hep.dataforge.vis.spatial.three
import hep.dataforge.meta.boolean import hep.dataforge.meta.boolean
import hep.dataforge.meta.get
import hep.dataforge.names.startsWith import hep.dataforge.names.startsWith
import hep.dataforge.names.toName
import hep.dataforge.provider.Type import hep.dataforge.provider.Type
import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.common.VisualObject
import hep.dataforge.vis.common.getProperty
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
@ -19,7 +18,7 @@ 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() = getProperty("material").material() internal val VisualObject.material get() = properties["material"].material()
/** /**
* Builder and updater for three.js object * Builder and updater for three.js object
@ -43,14 +42,14 @@ interface ThreeFactory<T : VisualObject> {
val mesh = Mesh(geometry, obj.material) val mesh = Mesh(geometry, obj.material)
//inherited edges definition, enabled by default //inherited edges definition, enabled by default
if (obj.getProperty("edges.enabled").boolean != false) { if (obj.properties["edges.enabled"].boolean != false) {
val material = obj.getProperty("edges.material")?.material() ?: Materials.DEFAULT val material = obj.properties["edges.material"]?.material() ?: 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.getProperty("wireframe.enabled").boolean == true) { if (obj.properties["wireframe.enabled"].boolean == true) {
val material = obj.getProperty("edges.material")?.material() ?: Materials.DEFAULT val material = obj.properties["edges.material"]?.material() ?: Materials.DEFAULT
mesh.add(LineSegments(WireframeGeometry(mesh.geometry as BufferGeometry), material)) mesh.add(LineSegments(WireframeGeometry(mesh.geometry as BufferGeometry), material))
} }
@ -63,9 +62,9 @@ interface ThreeFactory<T : VisualObject> {
//updated material //updated material
mesh.material = obj.material mesh.material = obj.material
} else if ( } else if (
name.startsWith("pos".toName()) || name.startsWith(PropertyNames3D.position) ||
name.startsWith("scale".toName()) || name.startsWith(PropertyNames3D.rotation) ||
name.startsWith("rotation".toName()) || name.startsWith(PropertyNames3D.scale) ||
name.toString() == "visible" name.toString() == "visible"
) { ) {
//update position of mesh using this object //update position of mesh using this object

View File

@ -38,7 +38,14 @@ class ThreePlugin : AbstractPlugin() {
fun buildObject3D(obj: VisualObject): Object3D { fun buildObject3D(obj: VisualObject): Object3D {
return when (obj) { return when (obj) {
is VisualGroup -> Group(obj.map { buildObject3D(it) }).apply { is VisualGroup -> Group(obj.mapNotNull {
try {
buildObject3D(it)
} catch (ex: Throwable){
logger.error(ex){"Failed to render $it"}
null
}
}).apply {
updatePosition(obj) updatePosition(obj)
} }
is Composite -> compositeFactory(obj) is Composite -> compositeFactory(obj)

View File

@ -1,23 +1,18 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.meta.EmptyMeta
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.DisplayLeaf
import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.common.VisualObject
import hep.dataforge.vis.common.double
class Box(parent: VisualObject?, meta: Meta) : DisplayLeaf(parent, meta), Shape { class Box(parent: VisualObject?, val xSize: Number, val ySize: Number, val zSize: Number, meta: Array<out Meta>) :
var xSize by double(100.0) VisualObject3D(parent, meta), Shape {
var ySize by double(100.0)
var zSize by double(100.0)
//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 / 2 val dx = xSize.toDouble() / 2
val dy = ySize / 2 val dy = ySize.toDouble() / 2
val dz = zSize / 2 val dz = zSize.toDouble() / 2
val node1 = Point3D(-dx, -dy, -dz) val node1 = Point3D(-dx, -dy, -dz)
val node2 = Point3D(dx, -dy, -dz) val node2 = Point3D(dx, -dy, -dz)
val node3 = Point3D(dx, dy, -dz) val node3 = Point3D(dx, dy, -dz)
@ -40,12 +35,15 @@ class Box(parent: VisualObject?, meta: Meta) : DisplayLeaf(parent, meta), Shape
} }
} }
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(xSize: Number, ySize: Number, zSize: Number, meta: Meta = EmptyMeta, action: Box.() -> Unit = {}) = fun VisualGroup.box(
Box(this, meta).apply(action).apply{ xSize: Number,
this.xSize = xSize.toDouble() ySize: Number,
this.ySize = ySize.toDouble() zSize: Number,
this.zSize = zSize.toDouble() name: String? = null,
}.also { add(it) } vararg meta: Meta,
action: Box.() -> Unit = {}
) =
Box(this, xSize, ySize, zSize, meta).apply(action).also { set(name, it) }

View File

@ -1,9 +1,8 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.meta.EmptyMeta
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.meta.seal import hep.dataforge.meta.seal
import hep.dataforge.vis.common.DisplayLeaf import hep.dataforge.meta.update
import hep.dataforge.vis.common.VisualGroup import hep.dataforge.vis.common.VisualGroup
import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.common.VisualObject
@ -13,28 +12,35 @@ enum class CompositeType {
SUBTRACT SUBTRACT
} }
class Composite( open class Composite(
parent: VisualObject?, parent: VisualObject?,
val first: VisualObject, val first: VisualObject,
val second: VisualObject, val second: VisualObject,
val type: CompositeType = CompositeType.UNION, val type: CompositeType = CompositeType.UNION,
meta: Meta = EmptyMeta meta: Array<out Meta>
) : DisplayLeaf(parent, meta) ) : VisualObject3D(parent, meta)
fun VisualGroup.composite(type: CompositeType, builder: VisualGroup.() -> Unit): Composite { fun VisualGroup.composite(
type: CompositeType,
name: String? = null,
vararg meta: Meta,
builder: VisualGroup.() -> Unit
): Composite {
val group = VisualGroup().apply(builder) val group = VisualGroup().apply(builder)
val children = group.toList() val children = group.toList()
if (children.size != 2) error("Composite requires exactly two children") if (children.size != 2) error("Composite requires exactly two children")
return Composite(this, children[0], children[1], type, group.properties.seal()).also { val groupMeta = group.properties.seal()
this.add(it) return Composite(this, children[0], children[1], type, meta).also {
it.config.update(groupMeta)
set(name, it)
} }
} }
fun VisualGroup.union(builder: VisualGroup.() -> Unit) = fun VisualGroup.union(name: String? = null, vararg meta: Meta, builder: VisualGroup.() -> Unit) =
composite(CompositeType.UNION,builder) composite(CompositeType.UNION, name, *meta, builder = builder)
fun VisualGroup.subtract(builder: VisualGroup.() -> Unit) = fun VisualGroup.subtract(name: String? = null, vararg meta: Meta, builder: VisualGroup.() -> Unit) =
composite(CompositeType.SUBTRACT,builder) composite(CompositeType.SUBTRACT, name, *meta, builder = builder)
fun VisualGroup.intersect(builder: VisualGroup.() -> Unit) = fun VisualGroup.intersect(name: String? = null, vararg meta: Meta, builder: VisualGroup.() -> Unit) =
composite(CompositeType.INTERSECT,builder) composite(CompositeType.INTERSECT, name, *meta, builder = builder)

View File

@ -1,26 +1,19 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.meta.* import hep.dataforge.meta.Meta
import hep.dataforge.vis.common.VisualGroup import hep.dataforge.vis.common.VisualGroup
import hep.dataforge.vis.common.DisplayLeaf import hep.dataforge.vis.common.VisualLeaf
import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.common.VisualObject
class Convex(parent: VisualObject?, meta: Meta) : DisplayLeaf(parent, meta) { class Convex(parent: VisualObject?, val points: List<Point3D>, meta: Array<out Meta>) : VisualLeaf(parent, meta) {
val points = points(properties["points"] ?: error("Vertices not defined"))
companion object { companion object {
const val TYPE = "geometry.3d.convex" const val TYPE = "geometry.3d.convex"
fun points(item: MetaItem<*>): List<Point3D> {
return item.node?.getAll("point")?.map { (_, value) ->
Point3D.from(value.node?: error("Point definition is not a node"))
} ?: emptyList()
}
} }
} }
fun VisualGroup.convex(meta: Meta = EmptyMeta, action: ConvexBuilder.() -> Unit = {}) = fun VisualGroup.convex(vararg meta: Meta, action: ConvexBuilder.() -> Unit = {}) =
ConvexBuilder().apply(action).build(this, meta).also { add(it) } ConvexBuilder().apply(action).build(this, meta).also { add(it) }
class ConvexBuilder { class ConvexBuilder {
@ -30,13 +23,7 @@ class ConvexBuilder {
points.add(Point3D(x, y, z)) points.add(Point3D(x, y, z))
} }
fun build(parent: VisualObject?, meta: Meta): Convex { fun build(parent: VisualObject?, meta: Array<out Meta>): Convex {
val points = buildMeta { return Convex(parent, points, meta)
points.forEachIndexed { index, value ->
"points.point[$index]" to value.toMeta()
}
}.seal()
return Convex(parent, points)
} }
} }

View File

@ -1,9 +1,8 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.meta.EmptyMeta
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.vis.common.DisplayLeaf
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
@ -11,18 +10,23 @@ import kotlin.math.PI
/** /**
* A cylinder or cut cone segment * A cylinder or cut cone segment
*/ */
class Cylinder(parent: VisualObject?, meta: Meta) : DisplayLeaf(parent, meta) { class Cylinder(parent: VisualObject?, radius: Number, height: Number, meta: Array<out Meta>) :
var radius by number() VisualLeaf(parent, meta) {
var upperRadius by number(default = radius) var radius by number(radius)
var height by number() 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)
} }
fun VisualGroup.cylinder(r: Number, height: Number, meta: Meta = EmptyMeta, block: Cylinder.() -> Unit = {}): Cylinder { fun VisualGroup.cylinder(
val cylinder = Cylinder(this, meta) r: Number,
cylinder.radius = r height: Number,
cylinder.height = height name: String? = null,
vararg meta: Meta,
block: Cylinder.() -> Unit = {}
): Cylinder {
val cylinder = Cylinder(this, r, height, meta)
cylinder.apply(block) cylinder.apply(block)
return cylinder.also { add(it) } return cylinder.also { set(name, it) }
} }

View File

@ -1,8 +1,8 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.meta.* import hep.dataforge.meta.Meta
import hep.dataforge.vis.common.DisplayLeaf
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
@ -18,7 +18,7 @@ class Shape2DBuilder {
list.add(Point2D(x, y)) list.add(Point2D(x, y))
} }
infix fun Number.to(y:Number) = point(this, y) infix fun Number.to(y: Number) = point(this, y)
fun build(): Shape2D = list fun build(): Shape2D = list
} }
@ -31,45 +31,22 @@ fun Shape2DBuilder.polygon(vertices: Int, radius: Number) {
} }
} }
class Layer(override val config: Config) : Specific { data class Layer(var x: Number, var y: Number, var z: Number, var scale: Number)
var z by number(0.0)
var x by number(0.0)
var y by number(0.0)
var scale by number(1.0)
companion object : Specification<Layer> { class Extruded(parent: VisualObject?, meta: Array<out Meta>) : VisualLeaf(parent, meta), Shape {
override fun wrap(config: Config): Layer = Layer(config)
}
}
//class Layer(val z: Number, val x: Number = 0.0, val y: Number = 0.0, val scale: Number = 1.0) var shape: List<Point2D> = ArrayList()
class Extruded(parent: VisualObject?, meta: Meta) : DisplayLeaf(parent, meta), Shape {
val shape
get() = properties.getAll("shape.point").map { (_, value) ->
Point2D.from(value.node ?: error("Point definition is not a node"))
}
fun shape(block: Shape2DBuilder.() -> Unit) { fun shape(block: Shape2DBuilder.() -> Unit) {
val points = Shape2DBuilder().apply(block).build().map { it.toMeta() } this.shape = Shape2DBuilder().apply(block).build()
properties["shape.point"] = points //TODO send invalidation signal
} }
val layers val layers: MutableList<Layer> = ArrayList()
get() = properties.getAll("layer").values.map {
Layer.wrap(it.node ?: error("layer item is not a node"))
}
fun layer(z: Number, x: Number = 0.0, y: Number = 0.0, scale: Number = 1.0): Layer { fun layer(z: Number, x: Number = 0.0, y: Number = 0.0, scale: Number = 1.0) {
val layer = Layer.build { layers.add(Layer(x,y,z,scale))
this.x = x //TODO send invalidation signal
this.y = y
this.z = z
this.scale = scale
}
properties.append("layer", layer)
return layer
} }
override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) { override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) {
@ -121,5 +98,5 @@ class Extruded(parent: VisualObject?, meta: Meta) : DisplayLeaf(parent, meta), S
} }
} }
fun VisualGroup.extrude(meta: Meta = EmptyMeta, action: Extruded.() -> Unit = {}) = fun VisualGroup.extrude(name: String? = null, vararg meta: Meta, action: Extruded.() -> Unit = {}) =
Extruded(this, meta).apply(action).also { add(it) } Extruded(this, meta).apply(action).also { set(name, it) }

View File

@ -1,14 +1,13 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.meta.EmptyMeta
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.DisplayLeaf 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.double
import kotlin.math.PI import kotlin.math.PI
class Sphere(parent: VisualObject?, meta: Meta) : DisplayLeaf(parent, meta) { class Sphere(parent: VisualObject?, meta: Array<out Meta>) : VisualLeaf(parent, meta) {
var radius by double(50.0) var radius by double(50.0)
var phiStart by double(0.0) var phiStart by double(0.0)
var phi by double(2 * PI) var phi by double(2 * PI)
@ -16,17 +15,15 @@ class Sphere(parent: VisualObject?, meta: Meta) : DisplayLeaf(parent, meta) {
var theta by double(PI) var theta by double(PI)
} }
fun VisualGroup.sphere(meta: Meta = EmptyMeta, action: Sphere.() -> Unit = {}) =
Sphere(this, meta).apply(action).also { add(it) }
fun VisualGroup.sphere( fun VisualGroup.sphere(
radius: Number, radius: Number,
phi: Number = 2 * PI, phi: Number = 2 * PI,
theta: Number = PI, theta: Number = PI,
meta: Meta = EmptyMeta, name: String? = null,
vararg meta: Meta,
action: Sphere.() -> Unit = {} action: Sphere.() -> Unit = {}
) = Sphere(this, meta).apply(action).apply { ) = Sphere(this, meta).apply(action).apply {
this.radius = radius.toDouble() this.radius = radius.toDouble()
this.phi = phi.toDouble() this.phi = phi.toDouble()
this.theta = theta.toDouble() this.theta = theta.toDouble()
}.also { add(it) } }.also { set(name, it) }

View File

@ -0,0 +1,196 @@
package hep.dataforge.vis.spatial
import hep.dataforge.meta.*
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
/**
* Performance optimized version of visual object
*/
open class VisualObject3D(parent: VisualObject?, tagRefs: Array<out Meta>) : VisualLeaf(parent, tagRefs) {
var x: Number? = null; get() = field ?: (this as VisualLeaf).x
var y: Number? = null; get() = field ?: (this as VisualLeaf).y
var z: Number? = null; get() = field ?: (this as VisualLeaf).z
var rotationX: Number? = null; get() = field ?: (this as VisualLeaf).rotationX
var rotationY: Number? = null; get() = field ?: (this as VisualLeaf).rotationY
var rotationZ: Number? = null; get() = field ?: (this as VisualLeaf).rotationZ
}
fun VisualGroup.group(key: String? = null, vararg meta: Meta, action: VisualGroup.() -> Unit = {}): VisualGroup =
VisualGroup(this, meta).apply(action).also { set(key, it) }
fun Output<VisualObject>.render(meta: Meta = EmptyMeta, action: VisualGroup.() -> Unit) =
render(VisualGroup().apply(action), meta)
//TODO replace properties by containers?
object PropertyNames3D {
val x = "x".asName()
val y = "y".asName()
val z = "z".asName()
val position = "pos".asName()
val xPos = position + x
val yPos = position + y
val zPos = position + z
val rotation = "rotation".asName()
val xRotation = rotation + x
val yRotation = rotation + y
val zRotation = rotation + z
val rotationOrder = rotation + "order"
val scale = "scale".asName()
val xScale = scale + x
val yScale = scale + y
val zScale = scale + z
}
// 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,
YZX,
ZXY,
XZY,
YXZ,
ZYX
}
/**
* Rotation order. Not inherited
*/
var VisualObject.rotationOrder: RotationOrder
get() = config[PropertyNames3D.rotationOrder].enum<RotationOrder>() ?: RotationOrder.XYZ
set(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
*/
var VisualObject.detail: Int?
get() = properties["detail"]?.int
set(value) {
config["detail"] = value
}
object World {
const val CAMERA_INITIAL_DISTANCE = -500.0
const val CAMERA_INITIAL_X_ANGLE = -50.0
const val CAMERA_INITIAL_Y_ANGLE = 0.0
const val CAMERA_INITIAL_Z_ANGLE = -210.0
const val CAMERA_NEAR_CLIP = 0.1
const val CAMERA_FAR_CLIP = 10000.0
}

View File

@ -1,164 +0,0 @@
package hep.dataforge.vis.spatial
import hep.dataforge.meta.*
import hep.dataforge.names.toName
import hep.dataforge.output.Output
import hep.dataforge.vis.common.VisualGroup
import hep.dataforge.vis.common.VisualObject
import hep.dataforge.vis.common.getProperty
fun VisualGroup.group(meta: Meta = EmptyMeta, action: VisualGroup.() -> Unit = {}): VisualGroup =
VisualGroup(this, meta).apply(action).also { add(it) }
fun Output<VisualObject>.render(meta: Meta = EmptyMeta, action: VisualGroup.() -> Unit) =
render(VisualGroup(null, EmptyMeta).apply(action), meta)
//TODO replace properties by containers?
// Common properties
/**
* Visibility property. Inherited from parent
*/
var VisualObject.visible
get() = getProperty("visible").boolean ?: true
set(value) {
properties["visible"] = value
}
// 3D Object position
private val xPos = "pos.x".toName()
/**
* x position property relative to parent. Not inherited
*/
var VisualObject.x
get() = properties[xPos].number ?: 0.0
set(value) {
properties[xPos] = value
}
private val yPos = "pos.y".toName()
/**
* y position property. Not inherited
*/
var VisualObject.y
get() = properties[yPos].number ?: 0.0
set(value) {
properties[yPos] = value
}
private val zPos = "pos.z".toName()
/**
* z position property. Not inherited
*/
var VisualObject.z
get() = properties[zPos].number ?: 0.0
set(value) {
properties[zPos] = value
}
// 3D Object rotation
private val xRotation = "rotation.x".toName()
/**
* x rotation relative to parent. Not inherited
*/
var VisualObject.rotationX
get() = properties[xRotation].number ?: 0.0
set(value) {
properties[xRotation] = value
}
private val yRotation = "rotation.y".toName()
/**
* y rotation relative to parent. Not inherited
*/
var VisualObject.rotationY
get() = properties[yRotation].number ?: 0.0
set(value) {
properties[yRotation] = value
}
private val zRotation = "rotation.z".toName()
/**
* z rotation relative to parent. Not inherited
*/
var VisualObject.rotationZ
get() = properties[zRotation].number ?: 0.0
set(value) {
properties[zRotation] = value
}
enum class RotationOrder {
XYZ,
YZX,
ZXY,
XZY,
YXZ,
ZYX
}
/**
* Rotation order. Not inherited
*/
var VisualObject.rotationOrder: RotationOrder
get() = getProperty("rotation.order").enum<RotationOrder>() ?: RotationOrder.XYZ
set(value) {
properties["rotation.order"] = value
}
// 3D object scale
/**
* X scale. Not inherited
*/
var VisualObject.scaleX
get() = properties["scale.x"].number ?: 1.0
set(value) {
properties["scale.x"] = value
}
/**
* Y scale. Not inherited
*/
var VisualObject.scaleY
get() = properties["scale.y"].number ?: 1.0
set(value) {
properties["scale.y"] = value
}
/**
* Z scale. Not inherited
*/
var VisualObject.scaleZ
get() = properties["scale.z"].number ?: 1.0
set(value) {
properties["scale.z"] = 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) {
properties["detail"] = value
}
object World {
const val CAMERA_INITIAL_DISTANCE = -500.0
const val CAMERA_INITIAL_X_ANGLE = -50.0
const val CAMERA_INITIAL_Y_ANGLE = 0.0
const val CAMERA_INITIAL_Z_ANGLE = -210.0
const val CAMERA_NEAR_CLIP = 0.1
const val CAMERA_FAR_CLIP = 10000.0
}

View File

@ -26,7 +26,7 @@ class ConvexTest {
val convex = group.first() as Convex val convex = group.first() as Convex
val pointsNode = convex.properties["points"].node val pointsNode = convex.config["points"].node
assertEquals(8, pointsNode?.items?.count()) assertEquals(8, pointsNode?.items?.count())
val points = pointsNode?.getAll("point".toName()) val points = pointsNode?.getAll("point".toName())

View File

@ -0,0 +1,52 @@
package hep.dataforge.vis.spatial
import hep.dataforge.vis.common.Colors
import hep.dataforge.vis.common.VisualGroup
import hep.dataforge.vis.common.color
import kotlin.math.PI
import kotlin.test.Test
import kotlin.test.assertEquals
class GroupTest {
@Test
fun testGroupWithComposite(){
val group = VisualGroup().apply{
union {
box(100, 100, 100) {
z = 100
rotationX = PI / 4
rotationY = PI / 4
}
box(100, 100, 100)
color {
"color" to Colors.lightgreen
"opacity" to 0.3
}
}
intersect{
box(100, 100, 100) {
z = 100
rotationX = PI / 4
rotationY = PI / 4
}
box(100, 100, 100)
y = 300
color(Colors.red)
}
subtract{
box(100, 100, 100) {
z = 100
rotationX = PI / 4
rotationY = PI / 4
}
box(100, 100, 100)
y = -300
color(Colors.blue)
}
}
assertEquals(3, group.count())
assertEquals(300.0,group.toList()[1].y.toDouble())
assertEquals(-300.0,group.toList()[2].y.toDouble())
}
}