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
*/
open class VisualLeaf(
parent: VisualObject? = null,
meta: Meta = EmptyMeta
) : AbstractVisualObject(parent), Configurable {
open class VisualLeaf(meta: Meta = EmptyMeta) : AbstractVisualObject(), Configurable {
val properties = Styled(meta)

View File

@ -1,12 +1,13 @@
package hep.dataforge.vis.common
import hep.dataforge.io.ConfigSerializer
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.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.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.
*/
val parent: VisualObject?
@Transient
var parent: VisualObject?
/**
* Set property for this object
@ -62,8 +64,10 @@ internal data class MetaListener(
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>()
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 }
}
@Serializable(ConfigSerializer::class)
@SerialName("properties")
private var _config: Config? = null
override val config: Config
get() = _config ?: Config().also { config ->
@ -103,117 +109,9 @@ abstract class AbstractVisualObject(override val parent: VisualObject?) : Visual
override fun toMeta(): Meta = buildMeta {
"type" to type
"properties" to _config
"properties" to config
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()
}
(node as? MutableMeta<*>)?.onChange(this, listener)
(node as? Config)?.onChange(this, listener)
nodeProperty.addListener { _, oldValue, newValue ->
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)
}
}

View File

@ -7,11 +7,11 @@ kotlin {
val commonMain by getting {
dependencies {
api(project(":dataforge-vis-spatial"))
api("scientifik:gdml:0.1.3")
api("scientifik:gdml:0.1.4-dev-1")
}
}
val jsMain by getting{
dependencies{
val jsMain by getting {
dependencies {
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 random = Random(111)
enum class Action{
ACCEPT,
REJECT,
CACHE
}
/**
* A special group for local templates
*/
@ -34,15 +40,15 @@ class GDMLTransformer(val root: GDML) {
}
}
var acceptSolid: (GDMLSolid) -> Boolean = { true }
var acceptGroup: (GDMLGroup) -> Boolean = { true }
var solidAction: (GDMLSolid) -> Action = { Action.CACHE }
var volumeAction: (GDMLGroup) -> Action = { Action.ACCEPT }
fun printStatistics() {
println("Solids:")
solidCounter.entries.sortedByDescending { it.value }.forEach {
println("\t$it")
}
println(println("Solids total: ${solidCounter.values.sum()}"))
println("Solids total: ${solidCounter.values.sum()}")
}
private val solidCounter = HashMap<String, Int>()
@ -53,8 +59,9 @@ class GDMLTransformer(val root: GDML) {
var onFinish: GDMLTransformer.() -> Unit = {}
internal fun finished() {
onFinish(this)
internal fun finished(final: VisualGroup3D) {
final.templates = templates
onFinish(this@GDMLTransformer)
}
}

View File

@ -1,6 +1,8 @@
package hep.dataforge.vis.spatial.gdml
import hep.dataforge.meta.Meta
import hep.dataforge.names.plus
import hep.dataforge.vis.common.asName
import hep.dataforge.vis.spatial.*
import scientifik.gdml.*
import kotlin.math.cos
@ -131,6 +133,8 @@ private fun VisualGroup3D.addSolid(
}.apply(block)
}
private val volumesName = "volumes".asName()
private fun VisualGroup3D.addPhysicalVolume(
context: GDMLTransformer,
physVolume: GDMLPhysVolume
@ -138,16 +142,36 @@ private fun VisualGroup3D.addPhysicalVolume(
val volume: GDMLGroup = physVolume.volumeref.resolve(context.root)
?: error("Volume with ref ${physVolume.volumeref.ref} could not be resolved")
if (context.acceptGroup(volume)) {
this[physVolume.name] = volume(
context,
volume,
when (context.volumeAction(volume)) {
GDMLTransformer.Action.ACCEPT -> {
this[physVolume.name] = volume(context, volume).apply {
withPosition(
context.lUnit,
physVolume.resolvePosition(context.root),
physVolume.resolveRotation(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(
@ -168,36 +192,38 @@ private fun VisualGroup3D.addDivisionVolume(
private fun VisualGroup3D.addVolume(
context: GDMLTransformer,
group: GDMLGroup,
position: GDMLPosition? = null,
rotation: GDMLRotation? = null,
scale: GDMLScale? = null
group: GDMLGroup
) {
this[group.name] = volume(context, group, position, rotation, scale)
this[group.name] = volume(context, group)
}
private fun volume(
context: GDMLTransformer,
group: GDMLGroup,
position: GDMLPosition? = null,
rotation: GDMLRotation? = null,
scale: GDMLScale? = null
group: GDMLGroup
): VisualGroup3D {
return VisualGroup3D().apply {
withPosition(context.lUnit, position, rotation, scale)
if (group is GDMLVolume) {
val solid = group.solidref.resolve(context.root)
?: error("Solid with tag ${group.solidref.ref} for volume ${group.name} not defined")
val material = group.materialref.resolve(context.root) ?: GDMLElement(group.materialref.ref)
if (context.acceptSolid(solid)) {
val cachedSolid = context.templates[solid.name]
?: context.templates.addSolid(context, solid, solid.name) {
when (context.solidAction(solid)) {
GDMLTransformer.Action.ACCEPT -> {
addSolid(context, solid, solid.name) {
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) {
@ -220,6 +246,6 @@ fun GDML.toVisual(block: GDMLTransformer.() -> Unit = {}): VisualGroup3D {
val context = GDMLTransformer(this).apply(block)
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.vis.hmr.ApplicationBase
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.toVisual
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")
canvas.clear()
val action: suspend CoroutineScope.(String) -> Unit = {
val action: suspend CoroutineScope.(String) -> Unit = { it ->
canvas.clear()
launch { spinner(true) }
launch { message("Loading GDML") }
@ -90,12 +91,16 @@ 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")
volumeAction = { volume ->
when {
volume.name.startsWith("ecal") -> GDMLTransformer.Action.REJECT
volume.name.startsWith("U") -> GDMLTransformer.Action.CACHE
volume.name.startsWith("V") -> GDMLTransformer.Action.CACHE
else -> GDMLTransformer.Action.ACCEPT
}
}
}
launch { message("Rendering") }
val output = three.output(canvas)
output.render(visual)

View File

@ -1,25 +1,38 @@
package hep.dataforge.vis.spatial.gdml
import hep.dataforge.names.toName
import hep.dataforge.vis.spatial.VisualGroup3D
import nl.adaptivity.xmlutil.StAXReader
import scientifik.gdml.GDML
import java.io.File
import java.net.URL
fun main() {
val url = URL("https://drive.google.com/open?id=1w5e7fILMN83JGgB8WANJUYm8OW2s0WVO")
val file = File("D:\\Work\\Projects\\gdml.kt\\gdml-source\\BM@N.gdml")
val stream = if (file.exists()) {
file.inputStream()
} else {
url.openStream()
}
//val file = File("D:\\Work\\Projects\\gdml.kt\\gdml-source\\cubes.gdml")
val xmlReader = StAXReader(stream, "UTF-8")
val xmlReader = StAXReader(file.inputStream(), "UTF-8")
val xml = GDML.format.parse(GDML.serializer(), xmlReader)
xml.toVisual {
val visual = xml.toVisual {
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() }
}
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)
}
is Composite -> compositeFactory(obj)
is Proxy3D -> proxyFactory(obj)
is Proxy -> proxyFactory(obj)
else -> {
//find specialized factory for this type if it is present
val factory = findObjectFactory(obj::class)
@ -84,7 +84,7 @@ class ThreePlugin : AbstractPlugin() {
}
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 fun invoke(meta: Meta) = ThreePlugin()
}

View File

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

View File

@ -2,13 +2,34 @@ plugins {
id("scientifik.mpp")
}
scientifik{
serialization = true
}
kotlin {
jvm{
withJava()
}
sourceSets {
val commonMain by getting {
commonMain {
dependencies {
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
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 kotlin.reflect.KClass
class Box(parent: VisualObject?, val xSize: Number, val ySize: Number, val zSize: Number) :
VisualLeaf3D(parent), Shape {
data class Box(
val xSize: Float,
val ySize: Float,
val zSize: Float
) : VisualLeaf3D(), Shape {
//TODO add helper for color configuration
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)
}
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"
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,
name: String? = null,
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.update
import hep.dataforge.vis.common.VisualObject
enum class CompositeType {
UNION,
@ -11,13 +10,12 @@ enum class CompositeType {
}
open class Composite(
parent: VisualObject?,
val first: VisualObject3D,
val second: VisualObject3D,
val compositeType: CompositeType = CompositeType.UNION
) : VisualLeaf3D(parent)
) : VisualLeaf3D()
fun VisualGroup3D.composite(
inline fun VisualGroup3D.composite(
type: CompositeType,
name: String? = null,
builder: VisualGroup3D.() -> Unit
@ -25,7 +23,7 @@ fun VisualGroup3D.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).also {
return Composite(children[0], children[1], type).also {
if (!group.config.isEmpty()) {
it.config.update(group.config)
}

View File

@ -1,17 +1,24 @@
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 {
const val TYPE = "geometry.3d.convex"
}
}
fun VisualGroup3D.convex(action: ConvexBuilder.() -> Unit = {}) =
ConvexBuilder().apply(action).build(this).also { add(it) }
inline fun VisualGroup3D.convex(name: String? = null, action: ConvexBuilder.() -> Unit = {}) =
ConvexBuilder().apply(action).build().also { set(name, it) }
class ConvexBuilder {
private val points = ArrayList<Point3D>()
@ -20,7 +27,7 @@ class ConvexBuilder {
points.add(Point3D(x, y, z))
}
fun build(parent: VisualObject?): Convex {
return Convex(parent, points)
fun build(): Convex {
return Convex(points)
}
}

View File

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

View File

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

View File

@ -1,21 +1,19 @@
package hep.dataforge.vis.spatial
import hep.dataforge.vis.common.VisualObject
import kotlin.math.PI
import kotlin.math.cos
import kotlin.math.sin
/**
* Stright tube segment
* Straight tube segment
*/
class Tube(
parent: VisualObject?,
var radius: Float,
var height: Float,
var innerRadius: Float = 0f,
var startAngle: Float = 0f,
var angle: Float = PI2
) : VisualLeaf3D(parent), Shape {
) : VisualLeaf3D(), Shape {
init {
require(radius > 0)
@ -108,7 +106,7 @@ class Tube(
}
}
fun VisualGroup3D.tube(
inline fun VisualGroup3D.tube(
r: Number,
height: Number,
innerRadius: Number = 0f,
@ -116,16 +114,12 @@ fun VisualGroup3D.tube(
angle: Number = 2 * PI,
name: String? = null,
block: Tube.() -> Unit = {}
): Tube {
val tube = Tube(
this,
): Tube = Tube(
r.toFloat(),
height.toFloat(),
innerRadius.toFloat(),
startAngle.toFloat(),
angle.toFloat()
).apply(
).apply(
block
)
return tube.also { set(name, it) }
}
).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
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
@ -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 rotation: Value3 = Value3()
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 rotation: Value3 = Value3()
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() {
updatePosition()
updateChildren()
@ -80,7 +92,7 @@ class VisualGroup3D(parent: VisualObject? = null) : VisualGroup<VisualObject3D>(
}
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) =
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
*/
var VisualObject3D.detail: Int?
get() = getProperty(DETAIL_KEY,false).int
get() = getProperty(DETAIL_KEY, false).int
set(value) = setProperty(DETAIL_KEY, value)
var VisualObject3D.material: Meta?

View File

@ -10,7 +10,7 @@ import kotlin.test.assertEquals
class ConvexTest {
@Test
fun testConvexBuilder() {
val group = VisualNode().apply {
val group = VisualGroup3D().apply {
convex {
point(50, 50, -50)
point(50, -50, -50)
@ -25,7 +25,7 @@ class ConvexTest {
val convex = group.first() as Convex
val pointsNode = convex.config["points"].node
val pointsNode = convex.toMeta()["points"].node
assertEquals(8, pointsNode?.items?.count())
val points = pointsNode?.getAll("point".toName())

View File

@ -1,7 +1,6 @@
package hep.dataforge.vis.spatial
import hep.dataforge.vis.common.Colors
import hep.dataforge.vis.common.color
import kotlin.math.PI
import kotlin.test.Test
import kotlin.test.assertEquals
@ -9,7 +8,7 @@ import kotlin.test.assertEquals
class GroupTest {
@Test
fun testGroupWithComposite(){
val group = VisualNode().apply{
val group = VisualGroup3D().apply{
union {
box(100, 100, 100) {
z = 100
@ -30,7 +29,7 @@ class GroupTest {
}
box(100, 100, 100)
y = 300
material(Colors.red)
color(Colors.red)
}
subtract{
box(100, 100, 100) {
@ -40,7 +39,7 @@ class GroupTest {
}
box(100, 100, 100)
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-js",
// ":dataforge-vis-jsroot",
":dataforge-vis-spatial-gdml"
":dataforge-vis-spatial-gdml",
":spatial-js-demo"
)
//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"))
// }
//}