Major caching update for objects

This commit is contained in:
Alexander Nozik 2019-08-04 11:02:36 +03:00
parent 073ae8a353
commit c3a7afc6d0
32 changed files with 553 additions and 292 deletions

View File

@ -0,0 +1,130 @@
package hep.dataforge.vis.common
import hep.dataforge.meta.MetaBuilder
import hep.dataforge.meta.MetaItem
import hep.dataforge.names.Name
import hep.dataforge.names.get
import hep.dataforge.provider.Provider
import kotlinx.serialization.Transient
import kotlin.collections.set
open class VisualGroup<T : VisualObject> : AbstractVisualObject(), 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) {
VisualObject.TYPE -> namedChildren
else -> emptyMap()
}
}
override fun propertyChanged(name: Name, before: MetaItem<*>?, after: MetaItem<*>?) {
super.propertyChanged(name, before, after)
forEach {
it.propertyChanged(name, before, after)
}
}
private data class Listener<T : VisualObject>(val owner: Any?, val callback: (Name?, T?) -> Unit)
@Transient
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 }
}
operator fun set(name: Name, child: T?) {
if (child == null) {
namedChildren.remove(name)
} else {
if (child.parent == null) {
child.parent = this
} else {
error("Can't reassign existing parent for $child")
}
namedChildren[name] = child
}
listeners.forEach { it.callback(name, child) }
}
/**
* 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 -> set(name, child)
child != null -> 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.get(key)
/**
* Get an unnamed child
*/
operator fun get(index: Int): T? = unnamedChildren[index]
/**
* Append unnamed child
*/
fun add(child: T) {
if (child.parent == null) {
child.parent = this
} else {
error("Can't reassign existing parent for $child")
}
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
"unnamedChildren" to unnamedChildren.map { it.toMeta() }
//adding named children
namedChildren.forEach {
"children[${it.key}]" to it.value.toMeta()
}
}
override fun MetaBuilder.updateMeta() {
updateChildren()
}
}

View File

@ -6,10 +6,7 @@ import hep.dataforge.names.Name
/** /**
* Basic [VisualObject] leaf element * Basic [VisualObject] leaf element
*/ */
open class VisualLeaf( open class VisualLeaf(meta: Meta = EmptyMeta) : AbstractVisualObject(), Configurable {
parent: VisualObject? = null,
meta: Meta = EmptyMeta
) : AbstractVisualObject(parent), Configurable {
val properties = Styled(meta) val properties = Styled(meta)

View File

@ -1,12 +1,13 @@
package hep.dataforge.vis.common package hep.dataforge.vis.common
import hep.dataforge.io.ConfigSerializer
import hep.dataforge.meta.* import hep.dataforge.meta.*
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.get
import hep.dataforge.provider.Provider
import hep.dataforge.provider.Type import hep.dataforge.provider.Type
import hep.dataforge.vis.common.VisualObject.Companion.TYPE import hep.dataforge.vis.common.VisualObject.Companion.TYPE
import kotlin.collections.set import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
private fun Laminate.withTop(meta: Meta): Laminate = Laminate(listOf(meta) + layers) private fun Laminate.withTop(meta: Meta): Laminate = Laminate(listOf(meta) + layers)
private fun Laminate.withBottom(meta: Meta): Laminate = Laminate(layers + meta) private fun Laminate.withBottom(meta: Meta): Laminate = Laminate(layers + meta)
@ -22,7 +23,8 @@ 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? @Transient
var parent: VisualObject?
/** /**
* Set property for this object * Set property for this object
@ -62,8 +64,10 @@ internal data class MetaListener(
val action: (name: Name, oldItem: MetaItem<*>?, newItem: MetaItem<*>?) -> Unit val action: (name: Name, oldItem: MetaItem<*>?, newItem: MetaItem<*>?) -> Unit
) )
abstract class AbstractVisualObject : VisualObject {
override var parent: VisualObject? = null
abstract class AbstractVisualObject(override val parent: VisualObject?) : VisualObject { @Transient
private val listeners = HashSet<MetaListener>() private val listeners = HashSet<MetaListener>()
override fun propertyChanged(name: Name, before: MetaItem<*>?, after: MetaItem<*>?) { override fun propertyChanged(name: Name, before: MetaItem<*>?, after: MetaItem<*>?) {
@ -80,6 +84,8 @@ abstract class AbstractVisualObject(override val parent: VisualObject?) : Visual
listeners.removeAll { it.owner == owner } listeners.removeAll { it.owner == owner }
} }
@Serializable(ConfigSerializer::class)
@SerialName("properties")
private var _config: Config? = null private var _config: Config? = null
override val config: Config override val config: Config
get() = _config ?: Config().also { config -> get() = _config ?: Config().also { config ->
@ -103,117 +109,9 @@ abstract class AbstractVisualObject(override val parent: VisualObject?) : Visual
override fun toMeta(): Meta = buildMeta { override fun toMeta(): Meta = buildMeta {
"type" to type "type" to type
"properties" to _config "properties" to config
updateMeta() updateMeta()
} }
} }
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()
}
}
override fun propertyChanged(name: Name, before: MetaItem<*>?, after: MetaItem<*>?) {
super.propertyChanged(name, before, after)
forEach {
it.propertyChanged(name, before, after)
}
}
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()
}
}

View File

@ -0,0 +1,37 @@
package hep.dataforge.vis.common
import hep.dataforge.context.*
import hep.dataforge.meta.Meta
import kotlin.reflect.KClass
interface VisualFactory<T : VisualObject> {
val type: KClass<T>
operator fun invoke(
context: Context,
parent: VisualObject?,
meta: Meta
): T
}
class VisualPlugin(meta: Meta) : AbstractPlugin(meta) {
override val tag: PluginTag get() = Companion.tag
/**
* Create a list of factories on first call and cache it
*/
val visualFactories by lazy {
context.content<VisualFactory<*>>(VISUAL_FACTORY_TYPE).mapKeys { it.value.type }
}
inline fun <reified T : VisualObject> buildVisual(parent: VisualObject?, meta: Meta): T? {
return visualFactories[T::class]?.invoke(context, parent, meta) as T?
}
companion object : PluginFactory<VisualPlugin> {
override val tag: PluginTag = PluginTag(name = "visual", group = PluginTag.DATAFORGE_GROUP)
override val type: KClass<out VisualPlugin> = VisualPlugin::class
override fun invoke(meta: Meta): VisualPlugin = VisualPlugin(meta)
const val VISUAL_FACTORY_TYPE = "visual.factory"
}
}

View File

@ -96,14 +96,14 @@ class FXMetaNode<M : MetaNode<M>>(
if (name.length == 1) invalidate() if (name.length == 1) invalidate()
} }
(node as? MutableMeta<*>)?.onChange(this, listener) (node as? Config)?.onChange(this, listener)
nodeProperty.addListener { _, oldValue, newValue -> nodeProperty.addListener { _, oldValue, newValue ->
if (newValue == null) { if (newValue == null) {
(oldValue as? MutableMeta<*>)?.removeListener(this) (oldValue as? Config)?.removeListener(this)
} }
if (newValue is MutableMeta<*>) { if (newValue is Config) {
newValue.onChange(this, listener) newValue.onChange(this, listener)
} }
} }

View File

@ -7,11 +7,11 @@ kotlin {
val commonMain by getting { val commonMain by getting {
dependencies { dependencies {
api(project(":dataforge-vis-spatial")) api(project(":dataforge-vis-spatial"))
api("scientifik:gdml:0.1.3") api("scientifik:gdml:0.1.4-dev-1")
} }
} }
val jsMain by getting{ val jsMain by getting {
dependencies{ dependencies {
api(project(":dataforge-vis-spatial-js")) api(project(":dataforge-vis-spatial-js"))
} }
} }

View File

@ -14,6 +14,12 @@ class GDMLTransformer(val root: GDML) {
private val materialCache = HashMap<GDMLMaterial, Meta>() private val materialCache = HashMap<GDMLMaterial, Meta>()
private val random = Random(111) private val random = Random(111)
enum class Action{
ACCEPT,
REJECT,
CACHE
}
/** /**
* A special group for local templates * A special group for local templates
*/ */
@ -34,15 +40,15 @@ class GDMLTransformer(val root: GDML) {
} }
} }
var acceptSolid: (GDMLSolid) -> Boolean = { true } var solidAction: (GDMLSolid) -> Action = { Action.CACHE }
var acceptGroup: (GDMLGroup) -> Boolean = { true } var volumeAction: (GDMLGroup) -> Action = { Action.ACCEPT }
fun printStatistics() { fun printStatistics() {
println("Solids:") println("Solids:")
solidCounter.entries.sortedByDescending { it.value }.forEach { solidCounter.entries.sortedByDescending { it.value }.forEach {
println("\t$it") println("\t$it")
} }
println(println("Solids total: ${solidCounter.values.sum()}")) println("Solids total: ${solidCounter.values.sum()}")
} }
private val solidCounter = HashMap<String, Int>() private val solidCounter = HashMap<String, Int>()
@ -53,8 +59,9 @@ class GDMLTransformer(val root: GDML) {
var onFinish: GDMLTransformer.() -> Unit = {} var onFinish: GDMLTransformer.() -> Unit = {}
internal fun finished() { internal fun finished(final: VisualGroup3D) {
onFinish(this) final.templates = templates
onFinish(this@GDMLTransformer)
} }
} }

View File

@ -1,6 +1,8 @@
package hep.dataforge.vis.spatial.gdml package hep.dataforge.vis.spatial.gdml
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.names.plus
import hep.dataforge.vis.common.asName
import hep.dataforge.vis.spatial.* import hep.dataforge.vis.spatial.*
import scientifik.gdml.* import scientifik.gdml.*
import kotlin.math.cos import kotlin.math.cos
@ -131,6 +133,8 @@ private fun VisualGroup3D.addSolid(
}.apply(block) }.apply(block)
} }
private val volumesName = "volumes".asName()
private fun VisualGroup3D.addPhysicalVolume( private fun VisualGroup3D.addPhysicalVolume(
context: GDMLTransformer, context: GDMLTransformer,
physVolume: GDMLPhysVolume physVolume: GDMLPhysVolume
@ -138,16 +142,36 @@ private fun VisualGroup3D.addPhysicalVolume(
val volume: GDMLGroup = physVolume.volumeref.resolve(context.root) val volume: GDMLGroup = physVolume.volumeref.resolve(context.root)
?: error("Volume with ref ${physVolume.volumeref.ref} could not be resolved") ?: error("Volume with ref ${physVolume.volumeref.ref} could not be resolved")
if (context.acceptGroup(volume)) { when (context.volumeAction(volume)) {
GDMLTransformer.Action.ACCEPT -> {
this[physVolume.name] = volume( this[physVolume.name] = volume(context, volume).apply {
context, withPosition(
volume, context.lUnit,
physVolume.resolvePosition(context.root), physVolume.resolvePosition(context.root),
physVolume.resolveRotation(context.root), physVolume.resolveRotation(context.root),
physVolume.resolveScale(context.root) physVolume.resolveScale(context.root)
) )
} }
}
GDMLTransformer.Action.CACHE -> {
val name = volumesName + volume.name.asName()
if (context.templates[name] == null) {
context.templates[name] = volume(context, volume)
}
this[physVolume.name] = Proxy(name).apply {
withPosition(
context.lUnit,
physVolume.resolvePosition(context.root),
physVolume.resolveRotation(context.root),
physVolume.resolveScale(context.root)
)
}
}
GDMLTransformer.Action.REJECT -> {
//ignore
}
}
} }
private fun VisualGroup3D.addDivisionVolume( private fun VisualGroup3D.addDivisionVolume(
@ -168,36 +192,38 @@ private fun VisualGroup3D.addDivisionVolume(
private fun VisualGroup3D.addVolume( private fun VisualGroup3D.addVolume(
context: GDMLTransformer, context: GDMLTransformer,
group: GDMLGroup, group: GDMLGroup
position: GDMLPosition? = null,
rotation: GDMLRotation? = null,
scale: GDMLScale? = null
) { ) {
this[group.name] = volume(context, group, position, rotation, scale) this[group.name] = volume(context, group)
} }
private fun volume( private fun volume(
context: GDMLTransformer, context: GDMLTransformer,
group: GDMLGroup, group: GDMLGroup
position: GDMLPosition? = null,
rotation: GDMLRotation? = null,
scale: GDMLScale? = null
): VisualGroup3D { ): VisualGroup3D {
return VisualGroup3D().apply { return VisualGroup3D().apply {
withPosition(context.lUnit, position, rotation, scale)
if (group is GDMLVolume) { if (group is GDMLVolume) {
val solid = group.solidref.resolve(context.root) val solid = group.solidref.resolve(context.root)
?: error("Solid with tag ${group.solidref.ref} for volume ${group.name} not defined") ?: error("Solid with tag ${group.solidref.ref} for volume ${group.name} not defined")
val material = group.materialref.resolve(context.root) ?: GDMLElement(group.materialref.ref) val material = group.materialref.resolve(context.root) ?: GDMLElement(group.materialref.ref)
if (context.acceptSolid(solid)) { when (context.solidAction(solid)) {
val cachedSolid = context.templates[solid.name] GDMLTransformer.Action.ACCEPT -> {
?: context.templates.addSolid(context, solid, solid.name) { addSolid(context, solid, solid.name) {
this.material = context.resolveColor(group, material, solid) this.material = context.resolveColor(group, material, solid)
} }
proxy(cachedSolid, solid.name) }
GDMLTransformer.Action.CACHE -> {
if (context.templates[solid.name] == null) {
context.templates.addSolid(context, solid, solid.name) {
this.material = context.resolveColor(group, material, solid)
}
}
ref(solid.name.asName(), solid.name)
}
GDMLTransformer.Action.REJECT -> {
//ignore
}
} }
when (val vol = group.placement) { when (val vol = group.placement) {
@ -220,6 +246,6 @@ fun GDML.toVisual(block: GDMLTransformer.() -> Unit = {}): VisualGroup3D {
val context = GDMLTransformer(this).apply(block) val context = GDMLTransformer(this).apply(block)
return volume(context, world).also { return volume(context, world).also {
context.finished() context.finished(it)
} }
} }

View File

@ -3,6 +3,7 @@ package hep.dataforge.vis.spatial.gdml.demo
import hep.dataforge.context.Global import hep.dataforge.context.Global
import hep.dataforge.vis.hmr.ApplicationBase import hep.dataforge.vis.hmr.ApplicationBase
import hep.dataforge.vis.hmr.startApplication import hep.dataforge.vis.hmr.startApplication
import hep.dataforge.vis.spatial.gdml.GDMLTransformer
import hep.dataforge.vis.spatial.gdml.LUnit import hep.dataforge.vis.spatial.gdml.LUnit
import hep.dataforge.vis.spatial.gdml.toVisual import hep.dataforge.vis.spatial.gdml.toVisual
import hep.dataforge.vis.spatial.three.ThreePlugin import hep.dataforge.vis.spatial.three.ThreePlugin
@ -82,7 +83,7 @@ private class GDMLDemoApp : ApplicationBase() {
val canvas = document.getElementById("canvas") ?: error("Element with id canvas not found on page") val canvas = document.getElementById("canvas") ?: error("Element with id canvas not found on page")
canvas.clear() canvas.clear()
val action: suspend CoroutineScope.(String) -> Unit = { val action: suspend CoroutineScope.(String) -> Unit = { it ->
canvas.clear() canvas.clear()
launch { spinner(true) } launch { spinner(true) }
launch { message("Loading GDML") } launch { message("Loading GDML") }
@ -90,12 +91,16 @@ private class GDMLDemoApp : ApplicationBase() {
launch { message("Converting GDML into DF-VIS format") } launch { message("Converting GDML into DF-VIS format") }
val visual = gdml.toVisual { val visual = gdml.toVisual {
lUnit = LUnit.CM lUnit = LUnit.CM
acceptSolid = { solid -> volumeAction = { volume ->
!solid.name.startsWith("ecal") when {
// && !solid.name.startsWith("V") volume.name.startsWith("ecal") -> GDMLTransformer.Action.REJECT
// && !solid.name.startsWith("U") volume.name.startsWith("U") -> GDMLTransformer.Action.CACHE
volume.name.startsWith("V") -> GDMLTransformer.Action.CACHE
else -> GDMLTransformer.Action.ACCEPT
} }
} }
}
launch { message("Rendering") } launch { message("Rendering") }
val output = three.output(canvas) val output = three.output(canvas)
output.render(visual) output.render(visual)

View File

@ -1,25 +1,38 @@
package hep.dataforge.vis.spatial.gdml package hep.dataforge.vis.spatial.gdml
import hep.dataforge.names.toName
import hep.dataforge.vis.spatial.VisualGroup3D
import nl.adaptivity.xmlutil.StAXReader import nl.adaptivity.xmlutil.StAXReader
import scientifik.gdml.GDML import scientifik.gdml.GDML
import java.io.File import java.io.File
import java.net.URL
fun main() { fun main() {
val url = URL("https://drive.google.com/open?id=1w5e7fILMN83JGgB8WANJUYm8OW2s0WVO")
val file = File("D:\\Work\\Projects\\gdml.kt\\gdml-source\\BM@N.gdml") val file = File("D:\\Work\\Projects\\gdml.kt\\gdml-source\\BM@N.gdml")
val stream = if (file.exists()) { //val file = File("D:\\Work\\Projects\\gdml.kt\\gdml-source\\cubes.gdml")
file.inputStream()
} else {
url.openStream()
}
val xmlReader = StAXReader(stream, "UTF-8") val xmlReader = StAXReader(file.inputStream(), "UTF-8")
val xml = GDML.format.parse(GDML.serializer(), xmlReader) val xml = GDML.format.parse(GDML.serializer(), xmlReader)
xml.toVisual { val visual = xml.toVisual {
lUnit = LUnit.CM lUnit = LUnit.CM
//acceptSolid = { solid -> !solid.name.startsWith("ecal") && !solid.name.startsWith("V") } volumeAction = { volume ->
when {
volume.name.startsWith("ecal") -> GDMLTransformer.Action.CACHE
volume.name.startsWith("U") -> GDMLTransformer.Action.CACHE
volume.name.startsWith("V") -> GDMLTransformer.Action.CACHE
else -> GDMLTransformer.Action.ACCEPT
}
}
onFinish = { printStatistics() } onFinish = { printStatistics() }
} }
readLine()
val template = visual.getTemplate("volumes.ecal01mod".toName())
println(template)
visual.flatMap { (it as? VisualGroup3D) ?: listOf(it) }.forEach {
if(it.parent==null) error("")
}
//readLine()
//val meta = visual.toMeta()
// val tmpFile = File.createTempFile("dataforge-visual", "json")
//tmpFile.writeText(meta.toString())
//println(tmpFile.absoluteFile)
} }

View File

@ -49,7 +49,7 @@ class ThreePlugin : AbstractPlugin() {
updatePosition(obj) updatePosition(obj)
} }
is Composite -> compositeFactory(obj) is Composite -> compositeFactory(obj)
is Proxy3D -> proxyFactory(obj) is Proxy -> proxyFactory(obj)
else -> { else -> {
//find specialized factory for this type if it is present //find specialized factory for this type if it is present
val factory = findObjectFactory(obj::class) val factory = findObjectFactory(obj::class)
@ -84,7 +84,7 @@ class ThreePlugin : AbstractPlugin() {
} }
companion object : PluginFactory<ThreePlugin> { companion object : PluginFactory<ThreePlugin> {
override val tag = PluginTag("vis.three", "hep.dataforge") override val tag = PluginTag("visual.three", PluginTag.DATAFORGE_GROUP)
override val type = ThreePlugin::class override val type = ThreePlugin::class
override fun invoke(meta: Meta) = ThreePlugin() override fun invoke(meta: Meta) = ThreePlugin()
} }

View File

@ -1,22 +1,22 @@
package hep.dataforge.vis.spatial.three package hep.dataforge.vis.spatial.three
import hep.dataforge.vis.spatial.Proxy3D import hep.dataforge.vis.spatial.Proxy
import hep.dataforge.vis.spatial.VisualObject3D import hep.dataforge.vis.spatial.VisualObject3D
import info.laht.threekt.core.Object3D import info.laht.threekt.core.Object3D
import info.laht.threekt.objects.Mesh
class ThreeProxyFactory(val three: ThreePlugin) : ThreeFactory<Proxy3D> { class ThreeProxyFactory(val three: ThreePlugin) : ThreeFactory<Proxy> {
private val cache = HashMap<VisualObject3D, Mesh>() private val cache = HashMap<VisualObject3D, Object3D>()
override val type = Proxy3D::class override val type = Proxy::class
override fun invoke(obj: Proxy3D): Object3D { override fun invoke(obj: Proxy): Object3D {
val templateMesh = cache.getOrPut(obj.template) { val template = obj.template
three.buildObject3D(obj.template) as Mesh val cachedObject = cache.getOrPut(template) {
three.buildObject3D(template)
} }
//val mesh = Mesh(templateMesh.geometry as BufferGeometry, templateMesh.material) //val mesh = Mesh(templateMesh.geometry as BufferGeometry, templateMesh.material)
val mesh = templateMesh.clone() val mesh = cachedObject.clone()
mesh.updatePosition(obj) mesh.updatePosition(obj)
return mesh return mesh

View File

@ -2,13 +2,34 @@ plugins {
id("scientifik.mpp") id("scientifik.mpp")
} }
scientifik{
serialization = true
}
kotlin { kotlin {
jvm{
withJava()
}
sourceSets { sourceSets {
val commonMain by getting { commonMain {
dependencies { dependencies {
api(project(":dataforge-vis-common")) api(project(":dataforge-vis-common"))
} }
} }
jvmMain{
dependencies {
}
}
jsMain{
dependencies {
api("info.laht.threekt:threejs-wrapper:0.106-npm-3")
implementation(npm("three", "0.106.2"))
implementation(npm("@hi-level/three-csg"))
implementation(npm("style-loader"))
implementation(npm("element-resize-event"))
}
}
} }
} }

View File

@ -1,9 +1,19 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.context.Context
import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaBuilder
import hep.dataforge.meta.float
import hep.dataforge.meta.get
import hep.dataforge.vis.common.VisualFactory
import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.common.VisualObject
import kotlin.reflect.KClass
class Box(parent: VisualObject?, val xSize: Number, val ySize: Number, val zSize: Number) : data class Box(
VisualLeaf3D(parent), Shape { val xSize: Float,
val ySize: Float,
val zSize: Float
) : VisualLeaf3D(), Shape {
//TODO add helper for color configuration //TODO add helper for color configuration
override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) { override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) {
@ -26,9 +36,24 @@ class Box(parent: VisualObject?, val xSize: Number, val ySize: Number, val zSize
geometryBuilder.face4(node8, node5, node6, node7) geometryBuilder.face4(node8, node5, node6, node7)
} }
companion object { override fun MetaBuilder.updateMeta() {
"xSize" to xSize
"ySize" to ySize
"zSize" to ySize
}
companion object : VisualFactory<Box> {
const val TYPE = "geometry.3d.box" const val TYPE = "geometry.3d.box"
override val type: KClass<Box> get() = Box::class
override fun invoke(context: Context, parent: VisualObject?, meta: Meta): Box = Box(
meta["xSize"].float!!,
meta["ySize"].float!!,
meta["zSize"].float!!
).apply {
update(meta)
}
} }
} }
@ -38,4 +63,4 @@ inline fun VisualGroup3D.box(
zSize: Number, zSize: Number,
name: String? = null, name: String? = null,
action: Box.() -> Unit = {} action: Box.() -> Unit = {}
) = Box(this, xSize, ySize, zSize).apply(action).also { set(name, it) } ) = Box(xSize.toFloat(), ySize.toFloat(), zSize.toFloat()).apply(action).also { set(name, it) }

View File

@ -2,7 +2,6 @@ package hep.dataforge.vis.spatial
import hep.dataforge.meta.isEmpty import hep.dataforge.meta.isEmpty
import hep.dataforge.meta.update import hep.dataforge.meta.update
import hep.dataforge.vis.common.VisualObject
enum class CompositeType { enum class CompositeType {
UNION, UNION,
@ -11,13 +10,12 @@ enum class CompositeType {
} }
open class Composite( open class Composite(
parent: VisualObject?,
val first: VisualObject3D, val first: VisualObject3D,
val second: VisualObject3D, val second: VisualObject3D,
val compositeType: CompositeType = CompositeType.UNION val compositeType: CompositeType = CompositeType.UNION
) : VisualLeaf3D(parent) ) : VisualLeaf3D()
fun VisualGroup3D.composite( inline fun VisualGroup3D.composite(
type: CompositeType, type: CompositeType,
name: String? = null, name: String? = null,
builder: VisualGroup3D.() -> Unit builder: VisualGroup3D.() -> Unit
@ -25,7 +23,7 @@ fun VisualGroup3D.composite(
val group = VisualGroup3D().apply(builder) val group = VisualGroup3D().apply(builder)
val children = group.filterIsInstance<VisualObject3D>() val children = group.filterIsInstance<VisualObject3D>()
if (children.size != 2) error("Composite requires exactly two children") if (children.size != 2) error("Composite requires exactly two children")
return Composite(this, children[0], children[1], type).also { return Composite(children[0], children[1], type).also {
if (!group.config.isEmpty()) { if (!group.config.isEmpty()) {
it.config.update(group.config) it.config.update(group.config)
} }

View File

@ -1,17 +1,24 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.vis.common.VisualObject import hep.dataforge.meta.MetaBuilder
class Convex(parent: VisualObject?, val points: List<Point3D>) : VisualLeaf3D(parent) { class Convex(
val points: List<Point3D>
) : VisualLeaf3D() {
override fun MetaBuilder.updateMeta() {
"points" to {
"point" to points.map{it.toMeta()}
}
}
companion object { companion object {
const val TYPE = "geometry.3d.convex" const val TYPE = "geometry.3d.convex"
} }
} }
fun VisualGroup3D.convex(action: ConvexBuilder.() -> Unit = {}) = inline fun VisualGroup3D.convex(name: String? = null, action: ConvexBuilder.() -> Unit = {}) =
ConvexBuilder().apply(action).build(this).also { add(it) } ConvexBuilder().apply(action).build().also { set(name, it) }
class ConvexBuilder { class ConvexBuilder {
private val points = ArrayList<Point3D>() private val points = ArrayList<Point3D>()
@ -20,7 +27,7 @@ class ConvexBuilder {
points.add(Point3D(x, y, z)) points.add(Point3D(x, y, z))
} }
fun build(parent: VisualObject?): Convex { fun build(): Convex {
return Convex(parent, points) return Convex(points)
} }
} }

View File

@ -1,27 +1,22 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.vis.common.VisualObject
import kotlin.math.PI
/** /**
* A cylinder or cut cone segment * A cylinder or cut cone segment
*/ */
class Cylinder( class Cylinder(
parent: VisualObject?, var radius: Float,
var radius: Number, var height: Float,
var height: Number, var upperRadius: Float = radius,
var upperRadius: Number = radius, var startAngle: Float = 0f,
var startAngle: Number = 0f, var angle: Float = PI2
var angle: Number = 2 * PI ) : VisualLeaf3D()
) : VisualLeaf3D(parent)
fun VisualGroup3D.cylinder( inline fun VisualGroup3D.cylinder(
r: Number, r: Number,
height: Number, height: Number,
name: String? = null, name: String? = null,
block: Cylinder.() -> Unit = {} block: Cylinder.() -> Unit = {}
): Cylinder { ): Cylinder = Cylinder(
val cylinder = Cylinder(this, r, height) r.toFloat(),
cylinder.apply(block) height.toFloat()
return cylinder.also { set(name, it) } ).apply(block).also { set(name, it) }
}

View File

@ -1,6 +1,5 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.vis.common.VisualObject
import kotlin.math.PI import kotlin.math.PI
import kotlin.math.cos import kotlin.math.cos
import kotlin.math.sin import kotlin.math.sin
@ -30,7 +29,7 @@ fun Shape2DBuilder.polygon(vertices: Int, radius: Number) {
data class Layer(var x: Number, var y: Number, var z: Number, var scale: Number) data class Layer(var x: Number, var y: Number, var z: Number, var scale: Number)
class Extruded(parent: VisualObject?) : VisualLeaf3D(parent), Shape { class Extruded : VisualLeaf3D(), Shape {
var shape: List<Point2D> = ArrayList() var shape: List<Point2D> = ArrayList()
@ -98,4 +97,4 @@ class Extruded(parent: VisualObject?) : VisualLeaf3D(parent), Shape {
} }
fun VisualGroup3D.extrude(name: String? = null, action: Extruded.() -> Unit = {}) = fun VisualGroup3D.extrude(name: String? = null, action: Extruded.() -> Unit = {}) =
Extruded(this).apply(action).also { set(name, it) } Extruded().apply(action).also { set(name, it) }

View File

@ -0,0 +1,54 @@
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
/**
* A proxy [VisualObject3D] to reuse a template object
*/
class Proxy(val templateName: Name) : AbstractVisualObject(), VisualObject3D {
override var position: Value3 = Value3()
override var rotation: Value3 = Value3()
override var scale: Value3 = Value3(1f, 1f, 1f)
val template by lazy { getTemplate() }
/**
* Recursively search for defined template in the parent
*/
private fun getTemplate(): VisualObject3D {
return (parent as? VisualGroup3D)?.getTemplate(templateName)
?: error("Template with name $templateName not found in $parent")
}
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() {
//TODO add reference to child
updatePosition()
}
}
//fun VisualGroup3D.proxy(
// templateName: Name,
// //name: String? = null,
// builder: VisualGroup3D.() -> Unit
//): Proxy {
// val template = getTemplate(templateName) ?: templates.builder()
// return Proxy(this, templateName).also { set(name, it) }
//}
inline fun VisualGroup3D.ref(
templateName: Name,
name: String? = null,
action: Proxy.() -> Unit = {}
) = Proxy(templateName).apply(action).also { set(name, it) }

View File

@ -1,34 +0,0 @@
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) }

View File

@ -1,23 +1,23 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.vis.common.VisualObject
import hep.dataforge.vis.common.number
import kotlin.math.PI import kotlin.math.PI
class Sphere(parent: VisualObject?, var radius: Number) : VisualLeaf3D(parent) { class Sphere(
var phiStart by number(0.0) var radius: Float,
var phi by number(2 * PI) var phiStart: Float = 0f,
var thetaStart by number(0.0) var phi: Float = PI2,
var theta by number(PI) var thetaStart: Float = 0f,
} var theta: Float = PI.toFloat()
) : VisualLeaf3D()
fun VisualGroup3D.sphere( inline fun VisualGroup3D.sphere(
radius: Number, radius: Number,
phi: Number = 2 * PI, phi: Number = 2 * PI,
theta: Number = PI, theta: Number = PI,
name: String? = null, name: String? = null,
action: Sphere.() -> Unit = {} action: Sphere.() -> Unit = {}
) = Sphere(this, radius).apply(action).apply { ) = Sphere(
this.phi = phi.toDouble() radius.toFloat(),
this.theta = theta.toDouble() phi = phi.toFloat(),
}.also { set(name, it) } theta = theta.toFloat()
).apply(action).also { set(name, it) }

View File

@ -1,21 +1,19 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.vis.common.VisualObject
import kotlin.math.PI import kotlin.math.PI
import kotlin.math.cos import kotlin.math.cos
import kotlin.math.sin import kotlin.math.sin
/** /**
* Stright tube segment * Straight tube segment
*/ */
class Tube( class Tube(
parent: VisualObject?,
var radius: Float, var radius: Float,
var height: Float, var height: Float,
var innerRadius: Float = 0f, var innerRadius: Float = 0f,
var startAngle: Float = 0f, var startAngle: Float = 0f,
var angle: Float = PI2 var angle: Float = PI2
) : VisualLeaf3D(parent), Shape { ) : VisualLeaf3D(), Shape {
init { init {
require(radius > 0) require(radius > 0)
@ -108,7 +106,7 @@ class Tube(
} }
} }
fun VisualGroup3D.tube( inline fun VisualGroup3D.tube(
r: Number, r: Number,
height: Number, height: Number,
innerRadius: Number = 0f, innerRadius: Number = 0f,
@ -116,16 +114,12 @@ fun VisualGroup3D.tube(
angle: Number = 2 * PI, angle: Number = 2 * PI,
name: String? = null, name: String? = null,
block: Tube.() -> Unit = {} block: Tube.() -> Unit = {}
): Tube { ): Tube = Tube(
val tube = Tube(
this,
r.toFloat(), r.toFloat(),
height.toFloat(), height.toFloat(),
innerRadius.toFloat(), innerRadius.toFloat(),
startAngle.toFloat(), startAngle.toFloat(),
angle.toFloat() angle.toFloat()
).apply( ).apply(
block block
) ).also { set(name, it) }
return tube.also { set(name, it) }
}

View File

@ -0,0 +1,40 @@
package hep.dataforge.vis.spatial
import hep.dataforge.context.AbstractPlugin
import hep.dataforge.context.PluginFactory
import hep.dataforge.context.PluginTag
import hep.dataforge.meta.*
import hep.dataforge.names.Name
import hep.dataforge.vis.common.VisualPlugin
import kotlin.reflect.KClass
class Visual3DPlugin(meta: Meta) : AbstractPlugin(meta) {
override val tag: PluginTag get() = Companion.tag
override fun provideTop(target: String): Map<Name, Any> {
return if (target == VisualPlugin.VISUAL_FACTORY_TYPE) {
mapOf()
} else {
emptyMap()
}
}
companion object : PluginFactory<Visual3DPlugin> {
override val tag: PluginTag = PluginTag(name = "visual.spatial", group = PluginTag.DATAFORGE_GROUP)
override val type: KClass<out Visual3DPlugin> = Visual3DPlugin::class
override fun invoke(meta: Meta): Visual3DPlugin = Visual3DPlugin(meta)
}
}
internal fun VisualObject3D.update(meta: Meta) {
fun Meta.toVector(default: Float = 0f) = Value3(
this[VisualObject3D.x].float ?: default,
this[VisualObject3D.y].float ?: default,
this[VisualObject3D.z].float ?: default
)
meta[VisualObject3D.position].node?.toVector()?.let { position = it }
meta[VisualObject3D.rotation].node?.toVector()?.let { rotation = it }
meta[VisualObject3D.scale].node?.toVector(1f)?.let { scale = it }
meta["properties"].node?.let { configure(it) }
}

View File

@ -1,6 +1,7 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.meta.* import hep.dataforge.meta.*
import hep.dataforge.names.Name
import hep.dataforge.names.plus import hep.dataforge.names.plus
import hep.dataforge.output.Output import hep.dataforge.output.Output
import hep.dataforge.vis.common.AbstractVisualObject import hep.dataforge.vis.common.AbstractVisualObject
@ -61,18 +62,29 @@ interface VisualObject3D : VisualObject {
} }
} }
abstract class VisualLeaf3D(parent: VisualObject?) : AbstractVisualObject(parent), VisualObject3D, Configurable { abstract class VisualLeaf3D : AbstractVisualObject(), VisualObject3D, Configurable {
override var position: Value3 = Value3() override var position: Value3 = Value3()
override var rotation: Value3 = Value3() override var rotation: Value3 = Value3()
override var scale: Value3 = Value3(1f, 1f, 1f) override var scale: Value3 = Value3(1f, 1f, 1f)
} }
class VisualGroup3D(parent: VisualObject? = null) : VisualGroup<VisualObject3D>(parent), VisualObject3D, Configurable { class VisualGroup3D : VisualGroup<VisualObject3D>(), VisualObject3D, Configurable {
override var position: Value3 = Value3() override var position: Value3 = Value3()
override var rotation: Value3 = Value3() override var rotation: Value3 = Value3()
override var scale: Value3 = Value3(1f, 1f, 1f) override var scale: Value3 = Value3(1f, 1f, 1f)
/**
* A container for templates visible inside this group
*/
var templates: VisualGroup3D? = null
set(value) {
value?.parent = this
field = value
}
fun getTemplate(name: Name): VisualObject3D? = templates?.get(name) ?: (parent as? VisualGroup3D)?.getTemplate(name)
override fun MetaBuilder.updateMeta() { override fun MetaBuilder.updateMeta() {
updatePosition() updatePosition()
updateChildren() updateChildren()
@ -80,7 +92,7 @@ class VisualGroup3D(parent: VisualObject? = null) : VisualGroup<VisualObject3D>(
} }
fun VisualGroup3D.group(key: String? = null, action: VisualGroup3D.() -> Unit = {}): VisualGroup3D = fun VisualGroup3D.group(key: String? = null, action: VisualGroup3D.() -> Unit = {}): VisualGroup3D =
VisualGroup3D(this).apply(action).also { set(key, it) } VisualGroup3D().apply(action).also { set(key, it) }
fun Output<VisualObject3D>.render(meta: Meta = EmptyMeta, action: VisualGroup3D.() -> Unit) = fun Output<VisualObject3D>.render(meta: Meta = EmptyMeta, action: VisualGroup3D.() -> Unit) =
render(VisualGroup3D().apply(action), meta) render(VisualGroup3D().apply(action), meta)
@ -108,7 +120,7 @@ var VisualObject3D.rotationOrder: RotationOrder
* Preferred number of polygons for displaying the object. If not defined, uses shape or renderer default. Not inherited * Preferred number of polygons for displaying the object. If not defined, uses shape or renderer default. Not inherited
*/ */
var VisualObject3D.detail: Int? var VisualObject3D.detail: Int?
get() = getProperty(DETAIL_KEY,false).int get() = getProperty(DETAIL_KEY, false).int
set(value) = setProperty(DETAIL_KEY, value) set(value) = setProperty(DETAIL_KEY, value)
var VisualObject3D.material: Meta? var VisualObject3D.material: Meta?

View File

@ -10,7 +10,7 @@ import kotlin.test.assertEquals
class ConvexTest { class ConvexTest {
@Test @Test
fun testConvexBuilder() { fun testConvexBuilder() {
val group = VisualNode().apply { val group = VisualGroup3D().apply {
convex { convex {
point(50, 50, -50) point(50, 50, -50)
point(50, -50, -50) point(50, -50, -50)
@ -25,7 +25,7 @@ class ConvexTest {
val convex = group.first() as Convex val convex = group.first() as Convex
val pointsNode = convex.config["points"].node val pointsNode = convex.toMeta()["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

@ -1,7 +1,6 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.vis.common.Colors import hep.dataforge.vis.common.Colors
import hep.dataforge.vis.common.color
import kotlin.math.PI import kotlin.math.PI
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -9,7 +8,7 @@ import kotlin.test.assertEquals
class GroupTest { class GroupTest {
@Test @Test
fun testGroupWithComposite(){ fun testGroupWithComposite(){
val group = VisualNode().apply{ val group = VisualGroup3D().apply{
union { union {
box(100, 100, 100) { box(100, 100, 100) {
z = 100 z = 100
@ -30,7 +29,7 @@ class GroupTest {
} }
box(100, 100, 100) box(100, 100, 100)
y = 300 y = 300
material(Colors.red) color(Colors.red)
} }
subtract{ subtract{
box(100, 100, 100) { box(100, 100, 100) {
@ -40,7 +39,7 @@ class GroupTest {
} }
box(100, 100, 100) box(100, 100, 100)
y = -300 y = -300
material(Colors.blue) color(Colors.blue)
} }
} }

View File

@ -0,0 +1,18 @@
package hep.dataforge.vis.spatial
import hep.dataforge.context.Global
import kotlin.test.Test
import kotlin.test.assertEquals
class SerializationTest {
@Test
fun testCubeSerialization(){
val cube = Box(null,100f,100f,100f).apply{
color(222)
}
val meta = cube.toMeta()
println(meta)
val newCube = Box(Global,null, meta)
assertEquals(cube,newCube)
}
}

View File

@ -37,7 +37,8 @@ include(
":dataforge-vis-spatial-fx", ":dataforge-vis-spatial-fx",
":dataforge-vis-spatial-js", ":dataforge-vis-spatial-js",
// ":dataforge-vis-jsroot", // ":dataforge-vis-jsroot",
":dataforge-vis-spatial-gdml" ":dataforge-vis-spatial-gdml",
":spatial-js-demo"
) )
//if(file("../dataforge-core").exists()) { //if(file("../dataforge-core").exists()) {

View File

@ -0,0 +1,19 @@
plugins {
id("scientifik.js")
//id("kotlin-dce-js")
}
dependencies {
api(project(":dataforge-vis-spatial-js"))
api("info.laht.threekt:threejs-wrapper:0.106-npm-3")
testCompile(kotlin("test-js"))
}
//kotlin{
// sourceSets["main"].dependencies{
// implementation(npm("three","0.106.2"))
// implementation(npm("@hi-level/three-csg"))
// implementation(npm("style-loader"))
// implementation(npm("element-resize-event"))
// }
//}