forked from kscience/visionforge
Added caching for geometries
This commit is contained in:
parent
997a5a8e60
commit
cf5a4fd7f6
@ -1,4 +1,4 @@
|
||||
val dataforgeVersion by extra("0.1.3-dev-9")
|
||||
val dataforgeVersion by extra("0.1.3-dev-10")
|
||||
|
||||
plugins{
|
||||
val kotlinVersion = "1.3.50-eap-5"
|
||||
|
@ -1,91 +0,0 @@
|
||||
package hep.dataforge.vis.common
|
||||
|
||||
import hep.dataforge.meta.Config
|
||||
import hep.dataforge.meta.Laminate
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.provider.Provider
|
||||
import kotlin.collections.set
|
||||
|
||||
/**
|
||||
* A display group which allows both named and unnamed children
|
||||
*/
|
||||
open class VisualGroup(
|
||||
override val parent: VisualObject? = null, tagRefs: Array<out Meta> = emptyArray()
|
||||
) : VisualObject, Iterable<VisualObject>, Provider {
|
||||
|
||||
private val namedChildren = HashMap<Name, VisualObject>()
|
||||
private val unnamedChildren = ArrayList<VisualObject>()
|
||||
|
||||
override val defaultTarget: String get() = VisualObject.TYPE
|
||||
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 provideTop(target: String): Map<Name, Any> {
|
||||
return when (target) {
|
||||
VisualObject.TYPE -> namedChildren
|
||||
else -> emptyMap()
|
||||
}
|
||||
}
|
||||
|
||||
private data class Listener(val owner: Any?, val callback: (Name?, VisualObject?) -> Unit)
|
||||
|
||||
private val listeners = HashSet<Listener>()
|
||||
|
||||
/**
|
||||
* Add listener for children change
|
||||
*/
|
||||
fun onChildrenChange(owner: Any?, action: (Name?, VisualObject?) -> Unit) {
|
||||
listeners.add(Listener(owner, action))
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove children change listener
|
||||
*/
|
||||
fun removeChildrenChangeListener(owner: Any?) {
|
||||
listeners.removeAll { it.owner === owner }
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(name: Name?, child: VisualObject?) {
|
||||
when {
|
||||
name != null -> {
|
||||
if (child == null) {
|
||||
namedChildren.remove(name)
|
||||
} else {
|
||||
namedChildren[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
|
||||
*/
|
||||
fun add(child: VisualObject) {
|
||||
unnamedChildren.add(child)
|
||||
listeners.forEach { it.callback(null, child) }
|
||||
}
|
||||
|
||||
/**
|
||||
* remove unnamed child
|
||||
*/
|
||||
fun remove(child: VisualObject) {
|
||||
unnamedChildren.remove(child)
|
||||
listeners.forEach { it.callback(null, null) }
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package hep.dataforge.vis.common
|
||||
|
||||
import hep.dataforge.meta.*
|
||||
import hep.dataforge.names.Name
|
||||
|
||||
/**
|
||||
* Basic [VisualObject] leaf element
|
||||
*/
|
||||
open class VisualLeaf(
|
||||
parent: VisualObject? = null,
|
||||
meta: Meta = EmptyMeta
|
||||
) : AbstractVisualObject(parent), Configurable {
|
||||
|
||||
val properties = Styled(meta)
|
||||
|
||||
override val config: Config = properties.style
|
||||
|
||||
override fun setProperty(name: Name, value: Any?) {
|
||||
config[name] = value
|
||||
}
|
||||
|
||||
override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? {
|
||||
return if (inherit) {
|
||||
properties[name] ?: parent?.getProperty(name, inherit)
|
||||
} else {
|
||||
properties[name]
|
||||
}
|
||||
}
|
||||
}
|
@ -2,10 +2,11 @@ package hep.dataforge.vis.common
|
||||
|
||||
import hep.dataforge.meta.*
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.get
|
||||
import hep.dataforge.provider.Provider
|
||||
import hep.dataforge.provider.Type
|
||||
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.TYPE
|
||||
import kotlin.collections.set
|
||||
|
||||
private fun Laminate.withTop(meta: Meta): Laminate = Laminate(listOf(meta) + layers)
|
||||
private fun Laminate.withBottom(meta: Meta): Laminate = Laminate(layers + meta)
|
||||
@ -16,88 +17,190 @@ private fun Laminate.withBottom(meta: Meta): Laminate = Laminate(layers + meta)
|
||||
@Type(TYPE)
|
||||
interface VisualObject : MetaRepr, Configurable {
|
||||
|
||||
val type: String get() = this::class.simpleName ?: TYPE
|
||||
|
||||
/**
|
||||
* The parent object of this one. If null, this one is a root.
|
||||
*/
|
||||
val parent: VisualObject?
|
||||
|
||||
/**
|
||||
* Individual properties configurator
|
||||
* Set property for this object
|
||||
*/
|
||||
override val config: Config
|
||||
fun setProperty(name: Name, value: Any?)
|
||||
|
||||
/**
|
||||
* All properties including inherited ones
|
||||
* Get property including or excluding parent properties
|
||||
*/
|
||||
val properties: Laminate
|
||||
fun getProperty(name: Name, inherit: Boolean = true): MetaItem<*>?
|
||||
|
||||
override fun toMeta(): Meta = buildMeta {
|
||||
"type" to this::class
|
||||
"properties" to properties
|
||||
}
|
||||
/**
|
||||
* Manually trigger property changed event. If [name] is empty, notify that the whole object is changed
|
||||
*/
|
||||
fun propertyChanged(name: Name, before: MetaItem<*>? = null, after: MetaItem<*>? = null): Unit
|
||||
|
||||
/**
|
||||
* Add listener triggering on property change
|
||||
*/
|
||||
fun onPropertyChange(owner: Any?, action: (Name, before: MetaItem<*>?, after: MetaItem<*>?) -> Unit): Unit
|
||||
|
||||
/**
|
||||
* Remove change listeners with given owner.
|
||||
*/
|
||||
fun removeChangeListener(owner: Any?)
|
||||
|
||||
companion object {
|
||||
const val TYPE = "visual"
|
||||
|
||||
const val DEFAULT_TYPE = ""
|
||||
//const val TYPE_KEY = "@type"
|
||||
//const val CHILDREN_KEY = "@children"
|
||||
const val META_KEY = "@meta"
|
||||
const val TAGS_KEY = "@tags"
|
||||
//const val META_KEY = "@meta"
|
||||
//const val TAGS_KEY = "@tags"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A change listener for [VisualObject] configuration.
|
||||
*/
|
||||
fun VisualObject.onChange(owner: Any?, action: (Name, before: MetaItem<*>?, after: MetaItem<*>?) -> Unit) {
|
||||
config.onChange(owner, action)
|
||||
parent?.onChange(owner, action)
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all meta listeners with matching owners
|
||||
*/
|
||||
fun VisualObject.removeChangeListener(owner: Any?) {
|
||||
config.removeListener(owner)
|
||||
parent?.removeChangeListener(owner)
|
||||
}
|
||||
internal data class MetaListener(
|
||||
val owner: Any? = null,
|
||||
val action: (name: Name, oldItem: MetaItem<*>?, newItem: MetaItem<*>?) -> Unit
|
||||
)
|
||||
|
||||
|
||||
/**
|
||||
* Additional meta not relevant to display
|
||||
*/
|
||||
val VisualObject.meta: Meta get() = config[META_KEY]?.node ?: EmptyMeta
|
||||
abstract class AbstractVisualObject(override val parent: VisualObject?) : VisualObject {
|
||||
private val listeners = HashSet<MetaListener>()
|
||||
|
||||
val VisualObject.tags: List<String> get() = config[TAGS_KEY].stringList
|
||||
override fun propertyChanged(name: Name, before: MetaItem<*>?, after: MetaItem<*>?) {
|
||||
for (l in listeners) {
|
||||
l.action(name, before, after)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic [VisualObject] leaf element
|
||||
*/
|
||||
open class VisualLeaf(
|
||||
final override val parent: VisualObject?,
|
||||
tagRefs: Array<out Meta>
|
||||
) : VisualObject {
|
||||
override val config = Config()
|
||||
override fun onPropertyChange(owner: Any?, action: (Name, before: MetaItem<*>?, after: MetaItem<*>?) -> Unit) {
|
||||
listeners.add(MetaListener(owner, action))
|
||||
}
|
||||
|
||||
override val properties: Laminate by lazy {
|
||||
combineProperties(parent, config, tagRefs)
|
||||
override fun removeChangeListener(owner: Any?) {
|
||||
listeners.removeAll { it.owner == owner }
|
||||
}
|
||||
|
||||
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?.get(name) ?: parent?.getProperty(name, inherit)
|
||||
} else {
|
||||
_config?.get(name)
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun MetaBuilder.updateMeta() {}
|
||||
|
||||
override fun toMeta(): Meta = buildMeta {
|
||||
"type" to type
|
||||
"properties" to _config
|
||||
updateMeta()
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
open class VisualGroup<T : VisualObject>(parent: VisualObject?) : AbstractVisualObject(parent), Iterable<T>, Provider {
|
||||
|
||||
protected val namedChildren = HashMap<Name, T>()
|
||||
protected val unnamedChildren = ArrayList<T>()
|
||||
|
||||
override val defaultTarget: String get() = VisualObject.TYPE
|
||||
|
||||
override fun iterator(): Iterator<T> = (namedChildren.values + unnamedChildren).iterator()
|
||||
|
||||
override fun provideTop(target: String): Map<Name, Any> {
|
||||
return when (target) {
|
||||
TYPE -> namedChildren
|
||||
else -> emptyMap()
|
||||
}
|
||||
}
|
||||
|
||||
private data class Listener<T : VisualObject>(val owner: Any?, val callback: (Name?, T?) -> Unit)
|
||||
|
||||
private val listeners = HashSet<Listener<T>>()
|
||||
|
||||
/**
|
||||
* Add listener for children change
|
||||
*/
|
||||
fun onChildrenChange(owner: Any?, action: (Name?, T?) -> Unit) {
|
||||
listeners.add(Listener(owner, action))
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove children change listener
|
||||
*/
|
||||
fun removeChildrenChangeListener(owner: Any?) {
|
||||
listeners.removeAll { it.owner === owner }
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(name: Name?, child: T?) {
|
||||
when {
|
||||
name != null -> {
|
||||
if (child == null) {
|
||||
namedChildren.remove(name)
|
||||
} else {
|
||||
namedChildren[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: T?) = set(key?.asName(), child)
|
||||
|
||||
/**
|
||||
* Get named child by name
|
||||
*/
|
||||
operator fun get(name: Name): T? = namedChildren[name]
|
||||
/**
|
||||
* Get named child by string
|
||||
*/
|
||||
operator fun get(key: String): T? = namedChildren[key]
|
||||
/**
|
||||
* Get an unnamed child
|
||||
*/
|
||||
operator fun get(index: Int): T? = unnamedChildren[index]
|
||||
|
||||
/**
|
||||
* Append unnamed child
|
||||
*/
|
||||
fun add(child: T) {
|
||||
unnamedChildren.add(child)
|
||||
listeners.forEach { it.callback(null, child) }
|
||||
}
|
||||
|
||||
/**
|
||||
* remove unnamed child
|
||||
*/
|
||||
fun remove(child: VisualObject) {
|
||||
unnamedChildren.remove(child)
|
||||
listeners.forEach { it.callback(null, null) }
|
||||
}
|
||||
|
||||
protected fun MetaBuilder.updateChildren() {
|
||||
//adding unnamed children
|
||||
"children" to unnamedChildren.map { it.toMeta() }
|
||||
//adding named children
|
||||
namedChildren.forEach {
|
||||
"children[${it.key}" to it.value.toMeta()
|
||||
}
|
||||
}
|
||||
|
||||
override fun MetaBuilder.updateMeta() {
|
||||
updateChildren()
|
||||
}
|
||||
}
|
||||
|
||||
///**
|
||||
// * A group that could contain both named and unnamed children. Unnamed children could be accessed only via
|
||||
// */
|
||||
//interface VisualGroup : DisplayObject, Iterable<DisplayObject>, Provider {
|
||||
// override val defaultTarget: String get() = DisplayObject.TARGET
|
||||
//
|
||||
// val children
|
||||
//}
|
||||
|
||||
|
@ -23,7 +23,7 @@ class DisplayObjectDelegate(
|
||||
override fun getValue(thisRef: VisualObject, property: KProperty<*>): MetaItem<*>? {
|
||||
val name = key ?: property.name.asName()
|
||||
return if (inherited) {
|
||||
thisRef.properties[name]
|
||||
thisRef.getProperty(name)
|
||||
} else {
|
||||
thisRef.config[name]
|
||||
} ?: default
|
||||
@ -48,7 +48,7 @@ class DisplayObjectDelegateWrapper<T>(
|
||||
override fun getValue(thisRef: VisualObject, property: KProperty<*>): T {
|
||||
val name = key ?: property.name.asName()
|
||||
return if (inherited) {
|
||||
read(thisRef.properties[name])
|
||||
read(thisRef.getProperty(name))
|
||||
} else {
|
||||
read(thisRef.config[name])
|
||||
} ?: default
|
||||
|
@ -3,7 +3,7 @@ package hep.dataforge.vis.spatial
|
||||
import hep.dataforge.context.Context
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.output.Output
|
||||
import hep.dataforge.vis.common.VisualGroup
|
||||
import hep.dataforge.vis.common.VisualNode
|
||||
import hep.dataforge.vis.common.VisualObject
|
||||
import javafx.scene.Group
|
||||
import javafx.scene.Node
|
||||
@ -27,7 +27,7 @@ class FX3DOutput(override val context: Context) : Output<VisualObject> {
|
||||
org.fxyz3d.geometry.Point3D(x.value ?: 0f, y.value ?: 0f, z.value ?: 0f)
|
||||
}
|
||||
return when (obj) {
|
||||
is VisualGroup -> Group(obj.map { buildNode(it) }).apply {
|
||||
is VisualNode -> Group(obj.map { buildNode(it) }).apply {
|
||||
this.translateXProperty().bind(x)
|
||||
this.translateYProperty().bind(y)
|
||||
this.translateZProperty().bind(z)
|
||||
|
@ -2,7 +2,6 @@ package hep.dataforge.vis.spatial
|
||||
|
||||
import hep.dataforge.context.Global
|
||||
import hep.dataforge.meta.number
|
||||
import hep.dataforge.vis.common.VisualGroup
|
||||
import javafx.scene.Parent
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.delay
|
||||
@ -21,7 +20,7 @@ class RendererDemoView : View() {
|
||||
center = renderer.canvas.root
|
||||
}
|
||||
|
||||
lateinit var group: VisualGroup
|
||||
lateinit var group: VisualGroup3D
|
||||
|
||||
init {
|
||||
|
||||
|
@ -0,0 +1,60 @@
|
||||
package hep.dataforge.vis.spatial.gdml
|
||||
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.buildMeta
|
||||
import hep.dataforge.meta.builder
|
||||
import hep.dataforge.vis.spatial.VisualGroup3D
|
||||
import scientifik.gdml.GDML
|
||||
import scientifik.gdml.GDMLGroup
|
||||
import scientifik.gdml.GDMLMaterial
|
||||
import scientifik.gdml.GDMLSolid
|
||||
import kotlin.random.Random
|
||||
|
||||
class GDMLTransformer(val root: GDML) {
|
||||
private val materialCache = HashMap<GDMLMaterial, Meta>()
|
||||
private val random = Random(111)
|
||||
|
||||
/**
|
||||
* A special group for local templates
|
||||
*/
|
||||
val templates by lazy { VisualGroup3D() }
|
||||
|
||||
var lUnit: LUnit = LUnit.MM
|
||||
var resolveColor: ColorResolver = { material, _ ->
|
||||
val materialColor = materialCache.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)
|
||||
}
|
||||
|
||||
}
|
@ -1,58 +1,10 @@
|
||||
package hep.dataforge.vis.spatial.gdml
|
||||
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.buildMeta
|
||||
import hep.dataforge.meta.builder
|
||||
import hep.dataforge.vis.spatial.*
|
||||
import scientifik.gdml.*
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
import kotlin.random.Random
|
||||
|
||||
|
||||
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(
|
||||
@ -82,7 +34,6 @@ private fun VisualObject3D.withPosition(
|
||||
private inline operator fun Number.times(d: Double) = toDouble() * d
|
||||
private inline operator fun Number.times(f: Float) = toFloat() * f
|
||||
|
||||
|
||||
private fun VisualGroup3D.addSolid(
|
||||
context: GDMLTransformer,
|
||||
solid: GDMLSolid,
|
||||
@ -238,9 +189,12 @@ private fun volume(
|
||||
val material = group.materialref.resolve(context.root) ?: GDMLElement(group.materialref.ref)
|
||||
|
||||
if (context.acceptSolid(solid)) {
|
||||
addSolid(context, solid, solid.name) {
|
||||
this.material = context.resolveColor(group, material, solid)
|
||||
}
|
||||
val cachedSolid = context.templates[solid.name]
|
||||
?: context.templates.addSolid(context, solid, solid.name) {
|
||||
this.material = context.resolveColor(group, material, solid)
|
||||
}
|
||||
val wrapper = Proxy3D(this,cachedSolid)
|
||||
add(wrapper)
|
||||
}
|
||||
|
||||
when (val vol = group.placement) {
|
||||
@ -262,7 +216,7 @@ fun GDML.toVisual(block: GDMLTransformer.() -> Unit = {}): VisualGroup3D {
|
||||
|
||||
val context = GDMLTransformer(this).apply(block)
|
||||
|
||||
return volume(context, world).also{
|
||||
return volume(context, world).also {
|
||||
context.finished()
|
||||
}
|
||||
}
|
@ -90,11 +90,11 @@ private class GDMLDemoApp : ApplicationBase() {
|
||||
launch { message("Converting GDML into DF-VIS format") }
|
||||
val visual = gdml.toVisual {
|
||||
lUnit = LUnit.CM
|
||||
acceptSolid = { solid ->
|
||||
!solid.name.startsWith("ecal")
|
||||
&& !solid.name.startsWith("V")
|
||||
&& !solid.name.startsWith("U")
|
||||
}
|
||||
// acceptSolid = { solid ->
|
||||
// !solid.name.startsWith("ecal")
|
||||
// && !solid.name.startsWith("V")
|
||||
// && !solid.name.startsWith("U")
|
||||
// }
|
||||
}
|
||||
launch { message("Rendering") }
|
||||
val output = three.output(canvas)
|
||||
|
@ -18,7 +18,7 @@ fun main() {
|
||||
val xml = GDML.format.parse(GDML.serializer(), xmlReader)
|
||||
xml.toVisual {
|
||||
lUnit = LUnit.CM
|
||||
acceptSolid = { solid -> !solid.name.startsWith("ecal") && !solid.name.startsWith("V") }
|
||||
//acceptSolid = { solid -> !solid.name.startsWith("ecal") && !solid.name.startsWith("V") }
|
||||
onFinish = { printStatistics() }
|
||||
}
|
||||
readLine()
|
||||
|
@ -40,21 +40,24 @@ fun MetaItem<*>.color(): Color {
|
||||
}
|
||||
}
|
||||
|
||||
private val materialCache = HashMap<Meta, Material>()
|
||||
|
||||
/**
|
||||
* Infer Three material based on meta item
|
||||
*/
|
||||
fun Meta?.jsMaterial(): Material {
|
||||
return if(this == null){
|
||||
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
|
||||
//TODO add more options for material
|
||||
return materialCache.getOrPut(this) {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@ class ThreeCompositeFactory(val three: ThreePlugin) : MeshThreeFactory<Composite
|
||||
second.updateMatrix()
|
||||
val firstCSG = CSG.fromMesh(first)
|
||||
val secondCSG = CSG.fromMesh(second)
|
||||
val resultCSG = when (obj.type) {
|
||||
val resultCSG = when (obj.compositeType) {
|
||||
CompositeType.UNION -> firstCSG.union(secondCSG)
|
||||
CompositeType.INTERSECT -> firstCSG.intersect(secondCSG)
|
||||
CompositeType.SUBTRACT -> firstCSG.subtract(secondCSG)
|
||||
|
@ -0,0 +1,11 @@
|
||||
package hep.dataforge.vis.spatial.three
|
||||
|
||||
import hep.dataforge.vis.spatial.Convex
|
||||
import info.laht.threekt.external.geometries.ConvexBufferGeometry
|
||||
|
||||
object ThreeConvexFactory : MeshThreeFactory<Convex>(Convex::class) {
|
||||
override fun buildGeometry(obj: Convex): ConvexBufferGeometry {
|
||||
val vectors = obj.points.map { it.asVector() }.toTypedArray()
|
||||
return ConvexBufferGeometry(vectors)
|
||||
}
|
||||
}
|
@ -1,18 +1,16 @@
|
||||
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.plus
|
||||
import hep.dataforge.names.startsWith
|
||||
import hep.dataforge.provider.Type
|
||||
import hep.dataforge.vis.common.onChange
|
||||
import hep.dataforge.vis.common.asName
|
||||
import hep.dataforge.vis.spatial.*
|
||||
import hep.dataforge.vis.spatial.three.ThreeFactory.Companion.TYPE
|
||||
import hep.dataforge.vis.spatial.three.ThreeFactory.Companion.buildMesh
|
||||
import info.laht.threekt.core.BufferGeometry
|
||||
import info.laht.threekt.core.Object3D
|
||||
import info.laht.threekt.external.geometries.ConvexBufferGeometry
|
||||
import info.laht.threekt.geometries.EdgesGeometry
|
||||
import info.laht.threekt.geometries.WireframeGeometry
|
||||
import info.laht.threekt.objects.LineSegments
|
||||
@ -31,55 +29,6 @@ interface ThreeFactory<T : VisualObject3D> {
|
||||
|
||||
companion object {
|
||||
const val TYPE = "threeFactory"
|
||||
|
||||
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.jsMaterial())
|
||||
|
||||
//inherited edges definition, enabled by default
|
||||
if (obj.properties["edges.enabled"].boolean != false) {
|
||||
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["wireframe.material"].node?.jsMaterial() ?: Materials.DEFAULT
|
||||
mesh.add(LineSegments(WireframeGeometry(mesh.geometry as BufferGeometry), material))
|
||||
}
|
||||
|
||||
//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.startsWith(VisualObject3D.materialKey)) {
|
||||
//updated material
|
||||
mesh.material = obj.material.jsMaterial()
|
||||
} else if (
|
||||
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.jsMaterial()
|
||||
}
|
||||
}
|
||||
return mesh
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,7 +39,30 @@ 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 ?: true
|
||||
updateMatrix()
|
||||
}
|
||||
|
||||
internal fun <T : VisualObject3D> Mesh.updateFrom(obj: T) {
|
||||
matrixAutoUpdate = false
|
||||
|
||||
//inherited edges definition, enabled by default
|
||||
if (obj.getProperty(MeshThreeFactory.EDGES_ENABLED_KEY).boolean != false) {
|
||||
val material = obj.getProperty(MeshThreeFactory.EDGES_MATERIAL_KEY).node?.jsMaterial() ?: Materials.DEFAULT
|
||||
add(LineSegments(EdgesGeometry(geometry as BufferGeometry), material))
|
||||
}
|
||||
|
||||
//inherited wireframe definition, disabled by default
|
||||
if (obj.getProperty(MeshThreeFactory.WIREFRAME_ENABLED_KEY).boolean == true) {
|
||||
val material = obj.getProperty(MeshThreeFactory.WIREFRAME_MATERIAL_KEY).node?.jsMaterial() ?: Materials.DEFAULT
|
||||
add(LineSegments(WireframeGeometry(geometry as BufferGeometry), material))
|
||||
}
|
||||
|
||||
//set position for mesh
|
||||
updatePosition(obj)
|
||||
|
||||
obj.getProperty(MeshThreeFactory.LAYER_KEY).int?.let {
|
||||
layers.set(it)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -118,6 +90,51 @@ abstract class MeshThreeFactory<T : VisualObject3D>(override val type: KClass<ou
|
||||
//create mesh from geometry
|
||||
return buildMesh<T>(obj) { buildGeometry(it) }
|
||||
}
|
||||
|
||||
companion object {
|
||||
val EDGES_KEY = "edges".asName()
|
||||
val WIREFRAME_KEY = "wireframe".asName()
|
||||
val ENABLED_KEY = "enabled".asName()
|
||||
val EDGES_ENABLED_KEY = EDGES_KEY + ENABLED_KEY
|
||||
val EDGES_MATERIAL_KEY = EDGES_KEY + VisualObject3D.MATERIAL_KEY
|
||||
val WIREFRAME_ENABLED_KEY = WIREFRAME_KEY + ENABLED_KEY
|
||||
val WIREFRAME_MATERIAL_KEY = WIREFRAME_KEY + VisualObject3D.MATERIAL_KEY
|
||||
val LAYER_KEY = "layer".asName()
|
||||
|
||||
fun <T : VisualObject3D> buildMesh(obj: T, geometryBuilder: (T) -> BufferGeometry): Mesh {
|
||||
//TODO add caching for geometries using templates
|
||||
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.jsMaterial())
|
||||
|
||||
mesh.updateFrom(obj)
|
||||
|
||||
//add listener to object properties
|
||||
obj.onPropertyChange(this) { name, _, _ ->
|
||||
if (name.startsWith(VisualObject3D.MATERIAL_KEY)) {
|
||||
//updated material
|
||||
mesh.material = obj.material.jsMaterial()
|
||||
} else if (
|
||||
name.startsWith(VisualObject3D.position) ||
|
||||
name.startsWith(VisualObject3D.rotation) ||
|
||||
name.startsWith(VisualObject3D.scale)
|
||||
) {
|
||||
//update position of mesh using this object
|
||||
mesh.updatePosition(obj)
|
||||
} else if (name == VisualObject3D.VISIBLE_KEY) {
|
||||
obj.visible = obj.visible ?: true
|
||||
} else {
|
||||
//full update
|
||||
mesh.geometry = geometryBuilder(obj)
|
||||
mesh.material = obj.material.jsMaterial()
|
||||
}
|
||||
}
|
||||
return mesh
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -129,12 +146,4 @@ object ThreeShapeFactory : MeshThreeFactory<Shape>(Shape::class) {
|
||||
ThreeGeometryBuilder().apply { toGeometry(this) }.build()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//FIXME not functional yet
|
||||
object ThreeConvexFactory : MeshThreeFactory<Convex>(Convex::class) {
|
||||
override fun buildGeometry(obj: Convex): ConvexBufferGeometry {
|
||||
val vectors = obj.points.map { it.asVector() }.toTypedArray()
|
||||
return ConvexBufferGeometry(vectors)
|
||||
}
|
||||
}
|
@ -20,6 +20,7 @@ class ThreePlugin : AbstractPlugin() {
|
||||
|
||||
private val objectFactories = HashMap<KClass<out VisualObject3D>, ThreeFactory<*>>()
|
||||
private val compositeFactory = ThreeCompositeFactory(this)
|
||||
private val proxyFactory = ThreeProxyFactory(this)
|
||||
|
||||
init {
|
||||
//Add specialized factories here
|
||||
@ -38,7 +39,6 @@ class ThreePlugin : AbstractPlugin() {
|
||||
return when (obj) {
|
||||
is VisualGroup3D -> Group(obj.mapNotNull {
|
||||
try {
|
||||
it as VisualObject3D
|
||||
buildObject3D(it)
|
||||
} catch (ex: Throwable) {
|
||||
console.error(ex)
|
||||
@ -49,6 +49,7 @@ class ThreePlugin : AbstractPlugin() {
|
||||
updatePosition(obj)
|
||||
}
|
||||
is Composite -> compositeFactory(obj)
|
||||
is Proxy3D -> proxyFactory(obj)
|
||||
else -> {
|
||||
//find specialized factory for this type if it is present
|
||||
val factory = findObjectFactory(obj::class)
|
||||
|
@ -0,0 +1,25 @@
|
||||
package hep.dataforge.vis.spatial.three
|
||||
|
||||
import hep.dataforge.vis.spatial.Proxy3D
|
||||
import hep.dataforge.vis.spatial.VisualObject3D
|
||||
import info.laht.threekt.core.BufferGeometry
|
||||
import info.laht.threekt.core.Object3D
|
||||
import info.laht.threekt.objects.Mesh
|
||||
|
||||
class ThreeProxyFactory(val three: ThreePlugin) : ThreeFactory<Proxy3D> {
|
||||
private val cache = HashMap<VisualObject3D, Mesh>()
|
||||
|
||||
override val type = Proxy3D::class
|
||||
|
||||
override fun invoke(obj: Proxy3D): Object3D {
|
||||
val templateMesh = cache.getOrPut(obj.template) {
|
||||
three.buildObject3D(obj.template) as Mesh
|
||||
}
|
||||
|
||||
val mesh = Mesh(templateMesh.geometry as BufferGeometry, templateMesh.material)
|
||||
//val mesh = templateMesh.clone()
|
||||
|
||||
mesh.updatePosition(obj)
|
||||
return mesh
|
||||
}
|
||||
}
|
@ -1,11 +1,9 @@
|
||||
package hep.dataforge.vis.spatial
|
||||
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.vis.common.VisualGroup
|
||||
import hep.dataforge.vis.common.VisualObject
|
||||
|
||||
class Box(parent: VisualObject?, val xSize: Number, val ySize: Number, val zSize: Number, meta: Array<out Meta>) :
|
||||
VisualLeaf3D(parent, meta), Shape {
|
||||
class Box(parent: VisualObject?, val xSize: Number, val ySize: Number, val zSize: Number) :
|
||||
VisualLeaf3D(parent), Shape {
|
||||
|
||||
//TODO add helper for color configuration
|
||||
override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) {
|
||||
@ -34,14 +32,10 @@ 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) }
|
||||
|
||||
inline fun VisualGroup.box(
|
||||
inline fun VisualGroup3D.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).apply(action).also { set(name, it) }
|
@ -1,9 +1,7 @@
|
||||
package hep.dataforge.vis.spatial
|
||||
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.isEmpty
|
||||
import hep.dataforge.meta.update
|
||||
import hep.dataforge.vis.common.VisualGroup
|
||||
import hep.dataforge.vis.common.VisualObject
|
||||
|
||||
enum class CompositeType {
|
||||
@ -16,21 +14,19 @@ open class Composite(
|
||||
parent: VisualObject?,
|
||||
val first: VisualObject3D,
|
||||
val second: VisualObject3D,
|
||||
val type: CompositeType = CompositeType.UNION,
|
||||
meta: Array<out Meta>
|
||||
) : VisualLeaf3D(parent, meta)
|
||||
val compositeType: CompositeType = CompositeType.UNION
|
||||
) : VisualLeaf3D(parent)
|
||||
|
||||
fun VisualGroup.composite(
|
||||
fun VisualGroup3D.composite(
|
||||
type: CompositeType,
|
||||
name: String? = null,
|
||||
vararg meta: Meta,
|
||||
builder: VisualGroup3D.() -> Unit
|
||||
): Composite {
|
||||
val group = VisualGroup3D().apply(builder)
|
||||
val children = group.filterIsInstance<VisualObject3D>()
|
||||
if (children.size != 2) error("Composite requires exactly two children")
|
||||
return Composite(this, children[0], children[1], type, meta).also {
|
||||
if( !group.config.isEmpty()) {
|
||||
return Composite(this, children[0], children[1], type).also {
|
||||
if (!group.config.isEmpty()) {
|
||||
it.config.update(group.config)
|
||||
}
|
||||
it.position = group.position
|
||||
@ -41,11 +37,11 @@ fun VisualGroup.composite(
|
||||
}
|
||||
}
|
||||
|
||||
fun VisualGroup3D.union(name: String? = null, vararg meta: Meta, builder: VisualGroup3D.() -> Unit) =
|
||||
composite(CompositeType.UNION, name, *meta, builder = builder)
|
||||
fun VisualGroup3D.union(name: String? = null, builder: VisualGroup3D.() -> Unit) =
|
||||
composite(CompositeType.UNION, name, builder = builder)
|
||||
|
||||
fun VisualGroup3D.subtract(name: String? = null, vararg meta: Meta, builder: VisualGroup3D.() -> Unit) =
|
||||
composite(CompositeType.SUBTRACT, name, *meta, builder = builder)
|
||||
fun VisualGroup3D.subtract(name: String? = null, builder: VisualGroup3D.() -> Unit) =
|
||||
composite(CompositeType.SUBTRACT, name, builder = builder)
|
||||
|
||||
fun VisualGroup3D.intersect(name: String? = null, vararg meta: Meta, builder: VisualGroup3D.() -> Unit) =
|
||||
composite(CompositeType.INTERSECT, name, *meta, builder = builder)
|
||||
fun VisualGroup3D.intersect(name: String? = null, builder: VisualGroup3D.() -> Unit) =
|
||||
composite(CompositeType.INTERSECT, name, builder = builder)
|
@ -1,10 +1,8 @@
|
||||
package hep.dataforge.vis.spatial
|
||||
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.vis.common.VisualGroup
|
||||
import hep.dataforge.vis.common.VisualObject
|
||||
|
||||
class Convex(parent: VisualObject?, val points: List<Point3D>, meta: Array<out Meta>) : VisualLeaf3D(parent, meta) {
|
||||
class Convex(parent: VisualObject?, val points: List<Point3D>) : VisualLeaf3D(parent) {
|
||||
|
||||
|
||||
companion object {
|
||||
@ -12,8 +10,8 @@ class Convex(parent: VisualObject?, val points: List<Point3D>, meta: Array<out M
|
||||
}
|
||||
}
|
||||
|
||||
fun VisualGroup.convex(vararg meta: Meta, action: ConvexBuilder.() -> Unit = {}) =
|
||||
ConvexBuilder().apply(action).build(this, meta).also { add(it) }
|
||||
fun VisualGroup3D.convex(action: ConvexBuilder.() -> Unit = {}) =
|
||||
ConvexBuilder().apply(action).build(this).also { add(it) }
|
||||
|
||||
class ConvexBuilder {
|
||||
private val points = ArrayList<Point3D>()
|
||||
@ -22,7 +20,7 @@ class ConvexBuilder {
|
||||
points.add(Point3D(x, y, z))
|
||||
}
|
||||
|
||||
fun build(parent: VisualObject?, meta: Array<out Meta>): Convex {
|
||||
return Convex(parent, points, meta)
|
||||
fun build(parent: VisualObject?): Convex {
|
||||
return Convex(parent, points)
|
||||
}
|
||||
}
|
@ -1,7 +1,5 @@
|
||||
package hep.dataforge.vis.spatial
|
||||
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.vis.common.VisualGroup
|
||||
import hep.dataforge.vis.common.VisualObject
|
||||
import hep.dataforge.vis.common.number
|
||||
import kotlin.math.PI
|
||||
@ -9,21 +7,19 @@ import kotlin.math.PI
|
||||
/**
|
||||
* A cylinder or cut cone segment
|
||||
*/
|
||||
class Cylinder(parent: VisualObject?, var radius: Number, var height: Number, meta: Array<out Meta>) :
|
||||
VisualLeaf3D(parent, meta) {
|
||||
class Cylinder(parent: VisualObject?, var radius: Number, var height: Number) : VisualLeaf3D(parent) {
|
||||
var upperRadius by number(radius)
|
||||
var startAngle by number(0.0)
|
||||
var angle by number(2 * PI)
|
||||
}
|
||||
|
||||
fun VisualGroup.cylinder(
|
||||
fun VisualGroup3D.cylinder(
|
||||
r: Number,
|
||||
height: Number,
|
||||
name: String? = null,
|
||||
vararg meta: Meta,
|
||||
block: Cylinder.() -> Unit = {}
|
||||
): Cylinder {
|
||||
val cylinder = Cylinder(this, r, height, meta)
|
||||
val cylinder = Cylinder(this, r, height)
|
||||
cylinder.apply(block)
|
||||
return cylinder.also { set(name, it) }
|
||||
}
|
@ -1,7 +1,5 @@
|
||||
package hep.dataforge.vis.spatial
|
||||
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.vis.common.VisualGroup
|
||||
import hep.dataforge.vis.common.VisualObject
|
||||
import kotlin.math.PI
|
||||
import kotlin.math.cos
|
||||
@ -32,7 +30,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>) : VisualLeaf3D(parent, meta), Shape {
|
||||
class Extruded(parent: VisualObject?) : VisualLeaf3D(parent), Shape {
|
||||
|
||||
var shape: List<Point2D> = ArrayList()
|
||||
|
||||
@ -112,5 +110,5 @@ class Extruded(parent: VisualObject?, meta: Array<out Meta>) : VisualLeaf3D(pare
|
||||
}
|
||||
}
|
||||
|
||||
fun VisualGroup.extrude(name: String? = null, vararg meta: Meta, action: Extruded.() -> Unit = {}) =
|
||||
Extruded(this, meta).apply(action).also { set(name, it) }
|
||||
fun VisualGroup3D.extrude(name: String? = null, action: Extruded.() -> Unit = {}) =
|
||||
Extruded(this).apply(action).also { set(name, it) }
|
@ -0,0 +1,34 @@
|
||||
package hep.dataforge.vis.spatial
|
||||
|
||||
import hep.dataforge.meta.MetaBuilder
|
||||
import hep.dataforge.meta.MetaItem
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.vis.common.AbstractVisualObject
|
||||
import hep.dataforge.vis.common.VisualObject
|
||||
|
||||
/**
|
||||
* A proxy [VisualObject3D] to reuse a [template] object
|
||||
*/
|
||||
class Proxy3D(parent: VisualObject?, val template: VisualObject3D) : AbstractVisualObject(parent), VisualObject3D {
|
||||
override var position: Value3 = Value3()
|
||||
override var rotation: Value3 = Value3()
|
||||
override var scale: Value3 = Value3(1f, 1f, 1f)
|
||||
|
||||
override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? {
|
||||
return if (inherit) {
|
||||
super.getProperty(name, false) ?: template.getProperty(name, false) ?: parent?.getProperty(name, inherit)
|
||||
} else {
|
||||
super.getProperty(name, false) ?: template.getProperty(name, false)
|
||||
}
|
||||
}
|
||||
|
||||
override fun MetaBuilder.updateMeta() {
|
||||
updatePosition()
|
||||
}
|
||||
}
|
||||
|
||||
inline fun VisualGroup3D.proxy(
|
||||
template: VisualObject3D,
|
||||
name: String? = null,
|
||||
action: Proxy3D.() -> Unit = {}
|
||||
) = Proxy3D(this, template).apply(action).also { set(name, it) }
|
@ -1,26 +1,23 @@
|
||||
package hep.dataforge.vis.spatial
|
||||
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.vis.common.VisualGroup
|
||||
import hep.dataforge.vis.common.VisualObject
|
||||
import hep.dataforge.vis.common.number
|
||||
import kotlin.math.PI
|
||||
|
||||
class Sphere(parent: VisualObject?, var radius: Number, meta: Array<out Meta>) : VisualLeaf3D(parent, meta) {
|
||||
class Sphere(parent: VisualObject?, var radius: Number) : VisualLeaf3D(parent) {
|
||||
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(
|
||||
fun VisualGroup3D.sphere(
|
||||
radius: Number,
|
||||
phi: Number = 2 * PI,
|
||||
theta: Number = PI,
|
||||
name: String? = null,
|
||||
vararg meta: Meta,
|
||||
action: Sphere.() -> Unit = {}
|
||||
) = Sphere(this, radius, meta).apply(action).apply {
|
||||
) = Sphere(this, radius).apply(action).apply {
|
||||
this.phi = phi.toDouble()
|
||||
this.theta = theta.toDouble()
|
||||
}.also { set(name, it) }
|
@ -1,16 +1,15 @@
|
||||
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.AbstractVisualObject
|
||||
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
|
||||
import hep.dataforge.vis.spatial.VisualObject3D.Companion.DETAIL_KEY
|
||||
import hep.dataforge.vis.spatial.VisualObject3D.Companion.MATERIAL_KEY
|
||||
import hep.dataforge.vis.spatial.VisualObject3D.Companion.VISIBLE_KEY
|
||||
|
||||
data class Value3(var x: Float = 0f, var y: Float = 0f, var z: Float = 0f)
|
||||
|
||||
@ -19,13 +18,22 @@ interface VisualObject3D : VisualObject {
|
||||
var rotation: Value3
|
||||
var scale: Value3
|
||||
|
||||
fun setProperty(name: Name, value: Any?)
|
||||
fun getProperty(name: Name, inherit: Boolean = true): MetaItem<*>?
|
||||
fun MetaBuilder.updatePosition() {
|
||||
xPos to position.x
|
||||
yPos to position.y
|
||||
zPos to position.z
|
||||
xRotation to rotation.x
|
||||
yRotation to rotation.y
|
||||
zRotation to rotation.z
|
||||
xScale to scale.x
|
||||
yScale to scale.y
|
||||
zScale to scale.z
|
||||
}
|
||||
|
||||
companion object {
|
||||
val materialKey = "material".asName()
|
||||
val visibleKey = "visible".asName()
|
||||
val detailKey = "detail".asName()
|
||||
val MATERIAL_KEY = "material".asName()
|
||||
val VISIBLE_KEY = "visible".asName()
|
||||
val DETAIL_KEY = "detail".asName()
|
||||
|
||||
val x = "x".asName()
|
||||
val y = "y".asName()
|
||||
@ -53,64 +61,32 @@ interface VisualObject3D : VisualObject {
|
||||
}
|
||||
}
|
||||
|
||||
open class VisualLeaf3D(parent: VisualObject?, tagRefs: Array<out Meta>) : VisualLeaf(parent, tagRefs), VisualObject3D {
|
||||
abstract class VisualLeaf3D(parent: VisualObject?) : AbstractVisualObject(parent), VisualObject3D, Configurable {
|
||||
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 {
|
||||
class VisualGroup3D(parent: VisualObject? = null) : VisualGroup<VisualObject3D>(parent), VisualObject3D, Configurable {
|
||||
|
||||
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)
|
||||
}
|
||||
override fun MetaBuilder.updateMeta() {
|
||||
updatePosition()
|
||||
updateChildren()
|
||||
}
|
||||
}
|
||||
|
||||
fun VisualGroup3D.group(key: String? = null, action: VisualGroup3D.() -> Unit = {}): VisualGroup3D =
|
||||
VisualGroup3D(this).apply(action).also { set(key, it) }
|
||||
|
||||
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) =
|
||||
fun Output<VisualObject3D>.render(meta: Meta = EmptyMeta, action: VisualGroup3D.() -> Unit) =
|
||||
render(VisualGroup3D().apply(action), meta)
|
||||
|
||||
|
||||
// Common properties
|
||||
|
||||
|
||||
enum class RotationOrder {
|
||||
XYZ,
|
||||
YZX,
|
||||
@ -132,16 +108,16 @@ var VisualObject3D.rotationOrder: RotationOrder
|
||||
* Preferred number of polygons for displaying the object. If not defined, uses shape or renderer default
|
||||
*/
|
||||
var VisualObject3D.detail: Int?
|
||||
get() = getProperty(detailKey).int
|
||||
set(value) = setProperty(detailKey, value)
|
||||
get() = getProperty(DETAIL_KEY).int
|
||||
set(value) = setProperty(DETAIL_KEY, value)
|
||||
|
||||
var VisualObject3D.material: Meta?
|
||||
get() = getProperty(materialKey).node
|
||||
set(value) = setProperty(materialKey, value)
|
||||
get() = getProperty(MATERIAL_KEY).node
|
||||
set(value) = setProperty(MATERIAL_KEY, value)
|
||||
|
||||
var VisualObject3D.visible: Boolean?
|
||||
get() = getProperty(visibleKey).boolean
|
||||
set(value) = setProperty(visibleKey, value)
|
||||
get() = getProperty(VISIBLE_KEY).boolean
|
||||
set(value) = setProperty(VISIBLE_KEY, value)
|
||||
|
||||
fun VisualObject3D.color(rgb: Int) {
|
||||
material = buildMeta { "color" to rgb }
|
||||
@ -157,11 +133,66 @@ fun VisualObject3D.color(r: Int, g: Int, b: Int) = material {
|
||||
"blue" to b
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
var VisualObject3D.x: Number
|
||||
get() = position.x
|
||||
set(value) {
|
||||
position.x = value.toFloat()
|
||||
propertyChanged(VisualObject3D.xPos)
|
||||
}
|
||||
|
||||
var VisualObject3D.y: Number
|
||||
get() = position.y
|
||||
set(value) {
|
||||
position.y = value.toFloat()
|
||||
propertyChanged(VisualObject3D.yPos)
|
||||
}
|
||||
|
||||
var VisualObject3D.z: Number
|
||||
get() = position.z
|
||||
set(value) {
|
||||
position.z = value.toFloat()
|
||||
propertyChanged(VisualObject3D.zPos)
|
||||
}
|
||||
|
||||
var VisualObject3D.rotationX: Number
|
||||
get() = rotation.x
|
||||
set(value) {
|
||||
rotation.x = value.toFloat()
|
||||
propertyChanged(VisualObject3D.xRotation)
|
||||
}
|
||||
|
||||
var VisualObject3D.rotationY: Number
|
||||
get() = rotation.y
|
||||
set(value) {
|
||||
rotation.y = value.toFloat()
|
||||
propertyChanged(VisualObject3D.xRotation)
|
||||
}
|
||||
|
||||
var VisualObject3D.rotationZ: Number
|
||||
get() = rotation.z
|
||||
set(value) {
|
||||
rotation.z = value.toFloat()
|
||||
propertyChanged(VisualObject3D.zRotation)
|
||||
}
|
||||
|
||||
var VisualObject3D.scaleX: Number
|
||||
get() = scale.x
|
||||
set(value) {
|
||||
scale.x = value.toFloat()
|
||||
propertyChanged(VisualObject3D.xScale)
|
||||
}
|
||||
|
||||
var VisualObject3D.scaleY: Number
|
||||
get() = scale.y
|
||||
set(value) {
|
||||
scale.y = value.toFloat()
|
||||
propertyChanged(VisualObject3D.yScale)
|
||||
}
|
||||
|
||||
var VisualObject3D.scaleZ: Number
|
||||
get() = scale.z
|
||||
set(value) {
|
||||
scale.z = value.toFloat()
|
||||
propertyChanged(VisualObject3D.zScale)
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,10 @@
|
||||
package hep.dataforge.vis.spatial
|
||||
|
||||
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
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
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()}
|
@ -4,14 +4,13 @@ import hep.dataforge.meta.get
|
||||
import hep.dataforge.meta.getAll
|
||||
import hep.dataforge.meta.node
|
||||
import hep.dataforge.names.toName
|
||||
import hep.dataforge.vis.common.VisualGroup
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class ConvexTest {
|
||||
@Test
|
||||
fun testConvexBuilder() {
|
||||
val group = VisualGroup().apply {
|
||||
val group = VisualNode().apply {
|
||||
convex {
|
||||
point(50, 50, -50)
|
||||
point(50, -50, -50)
|
||||
|
@ -1,7 +1,6 @@
|
||||
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
|
||||
@ -10,7 +9,7 @@ import kotlin.test.assertEquals
|
||||
class GroupTest {
|
||||
@Test
|
||||
fun testGroupWithComposite(){
|
||||
val group = VisualGroup().apply{
|
||||
val group = VisualNode().apply{
|
||||
union {
|
||||
box(100, 100, 100) {
|
||||
z = 100
|
||||
|
Loading…
Reference in New Issue
Block a user