diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/Colors.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/Colors.kt index 340caa59..bfb51fd4 100644 --- a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/Colors.kt +++ b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/Colors.kt @@ -4,6 +4,7 @@ import hep.dataforge.meta.Meta import hep.dataforge.meta.MetaBuilder import hep.dataforge.meta.buildMeta import hep.dataforge.meta.set +import hep.dataforge.names.toName /** * Taken from https://github.com/markaren/three.kt/blob/master/threejs-wrapper/src/main/kotlin/info/laht/threekt/math/ColorConstants.kt @@ -181,13 +182,14 @@ object Colors { const val yellowgreen = 0x9ACD32 } +private val material = "material".toName() fun VisualObject.color(rgb: Int) { - this.properties["material"] = rgb + this.config[material] = rgb } fun VisualObject.color(meta: Meta) { - this.properties["material"] = meta + this.config[material] = meta } fun VisualObject.color(builder: MetaBuilder.() -> Unit) { diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualGroup.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualGroup.kt index e5cb4b47..66fdaec3 100644 --- a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualGroup.kt +++ b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualGroup.kt @@ -1,29 +1,41 @@ package hep.dataforge.vis.common -import hep.dataforge.meta.EmptyMeta +import hep.dataforge.meta.Config +import hep.dataforge.meta.Laminate import hep.dataforge.meta.Meta -import hep.dataforge.meta.Styled import hep.dataforge.names.Name -import hep.dataforge.names.toName import hep.dataforge.provider.Provider +import kotlin.collections.ArrayList +import kotlin.collections.HashMap +import kotlin.collections.HashSet +import kotlin.collections.Iterable +import kotlin.collections.Iterator +import kotlin.collections.Map +import kotlin.collections.emptyMap +import kotlin.collections.forEach +import kotlin.collections.plus +import kotlin.collections.removeAll +import kotlin.collections.set /** * A display group which allows both named and unnamed children */ class VisualGroup( - override val parent: VisualObject? = null, meta: Meta = EmptyMeta + override val parent: VisualObject? = null, tagRefs: Array = emptyArray() ) : VisualObject, Iterable, Provider { private val namedChildren = HashMap() private val unnamedChildren = ArrayList() override val defaultTarget: String get() = VisualObject.TYPE - override val properties: Styled = Styled(meta) + override val config = Config() + + override val properties: Laminate by lazy { combineProperties(parent, config, tagRefs) } override fun iterator(): Iterator = (namedChildren.values + unnamedChildren).iterator() override fun provideTop(target: String): Map { - return when(target){ + return when (target) { VisualObject.TYPE -> namedChildren else -> emptyMap() } @@ -49,22 +61,26 @@ class VisualGroup( } /** - * + * Add named or unnamed child to the group. If key is [null] the child is considered unnamed. Both key and value are not + * allowed to be null in the same time. If name is present and [child] is null, the appropriate element is removed. */ - operator fun set(key: String?, child: VisualObject?) { - if(key == null){ - - } else { - val name = key.toName() - if (child == null) { - namedChildren.remove(name) - } else { - namedChildren[name] = child + 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) } } - 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 */ diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualObject.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualObject.kt index 40e877ba..0802b20f 100644 --- a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualObject.kt +++ b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualObject.kt @@ -2,24 +2,34 @@ package hep.dataforge.vis.common import hep.dataforge.meta.* import hep.dataforge.names.Name -import hep.dataforge.names.toName 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 +private fun Laminate.withTop(meta: Meta): Laminate = Laminate(listOf(meta) + layers) +private fun Laminate.withBottom(meta: Meta): Laminate = Laminate(layers + meta) + /** * A root type for display hierarchy */ @Type(TYPE) -interface VisualObject : MetaRepr { +interface VisualObject : MetaRepr, Configurable { /** * The parent object of this one. If null, this one is a root. */ val parent: VisualObject? - val properties: Styled + /** + * Individual properties configurator + */ + override val config: Config + + /** + * All properties including inherited ones + */ + val properties: Laminate override fun toMeta(): Meta = buildMeta { "type" to this::class @@ -37,18 +47,11 @@ interface VisualObject : MetaRepr { } } -/** - * Get the property of this display object of parent's if not found - */ -tailrec fun VisualObject.getProperty(name: Name): MetaItem<*>? = properties[name] ?: parent?.getProperty(name) - -fun VisualObject.getProperty(name: String): MetaItem<*>? = getProperty(name.toName()) - /** * A change listener for [VisualObject] configuration. */ fun VisualObject.onChange(owner: Any?, action: (Name, before: MetaItem<*>?, after: MetaItem<*>?) -> Unit) { - properties.onChange(owner, action) + config.onChange(owner, action) parent?.onChange(owner, action) } @@ -56,7 +59,7 @@ fun VisualObject.onChange(owner: Any?, action: (Name, before: MetaItem<*>?, afte * Remove all meta listeners with matching owners */ fun VisualObject.removeChangeListener(owner: Any?) { - properties.removeListener(owner) + config.removeListener(owner) parent?.removeChangeListener(owner) } @@ -64,18 +67,28 @@ fun VisualObject.removeChangeListener(owner: Any?) { /** * Additional meta not relevant to display */ -val VisualObject.meta: Meta get() = properties[META_KEY]?.node ?: EmptyMeta +val VisualObject.meta: Meta get() = config[META_KEY]?.node ?: EmptyMeta -val VisualObject.tags: List get() = properties[TAGS_KEY].stringList +val VisualObject.tags: List get() = config[TAGS_KEY].stringList /** * Basic [VisualObject] leaf element */ -open class DisplayLeaf( - override val parent: VisualObject?, - meta: Meta = EmptyMeta +open class VisualLeaf( + final override val parent: VisualObject?, + tagRefs: Array ) : VisualObject { - final override val properties = Styled(meta) + final override val config = Config() + + override val properties: Laminate by lazy { combineProperties(parent, config, tagRefs) } +} + +internal fun combineProperties(parent: VisualObject?, config: Config, tagRefs: Array): Laminate { + val list = ArrayList(tagRefs.size + 2) + list += config + list.addAll(tagRefs) + parent?.properties?.let { list.add(it) } + return Laminate(list) } ///** diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualObjectDelegates.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualObjectDelegates.kt index e3c42de4..b951e587 100644 --- a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualObjectDelegates.kt +++ b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualObjectDelegates.kt @@ -2,13 +2,16 @@ package hep.dataforge.vis.common import hep.dataforge.meta.* import hep.dataforge.names.Name -import hep.dataforge.names.toName +import hep.dataforge.names.NameToken +import hep.dataforge.names.asName import hep.dataforge.values.Value import kotlin.jvm.JvmName import kotlin.properties.ReadOnlyProperty import kotlin.properties.ReadWriteProperty import kotlin.reflect.KProperty +fun String.asName() = NameToken(this).asName() + /** * A delegate for display object properties */ @@ -18,17 +21,17 @@ class DisplayObjectDelegate( val inherited: Boolean ) : ReadWriteProperty?> { override fun getValue(thisRef: VisualObject, property: KProperty<*>): MetaItem<*>? { - val name = key ?: property.name.toName() + val name = key ?: property.name.asName() return if (inherited) { - thisRef.getProperty(name) - } else { thisRef.properties[name] + } else { + thisRef.config[name] } ?: default } override fun setValue(thisRef: VisualObject, property: KProperty<*>, value: MetaItem<*>?) { - val name = key ?: property.name.toName() - thisRef.properties[name] = value + val name = key ?: property.name.asName() + thisRef.config[name] = value } } @@ -43,70 +46,71 @@ class DisplayObjectDelegateWrapper( //private var cachedName: Name? = null override fun getValue(thisRef: VisualObject, property: KProperty<*>): T { - val name = key ?: property.name.toName() + val name = key ?: property.name.asName() return if (inherited) { - read(thisRef.getProperty(name)) - } else { read(thisRef.properties[name]) + } else { + read(thisRef.config[name]) } ?: default } override fun setValue(thisRef: VisualObject, property: KProperty<*>, value: T) { - val name = key ?: property.name.toName() - thisRef.properties[name] = value + val name = key ?: property.name.asName() + thisRef.config[name] = value } } fun VisualObject.value(default: Value? = null, key: String? = null, inherited: Boolean = false) = - DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.value } + DisplayObjectDelegateWrapper(key?.asName(), default, inherited) { it.value } fun VisualObject.string(default: String? = null, key: String? = null, inherited: Boolean = false) = - DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.string } + DisplayObjectDelegateWrapper(key?.asName(), default, inherited) { it.string } fun VisualObject.boolean(default: Boolean? = null, key: String? = null, inherited: Boolean = false) = - DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.boolean } + DisplayObjectDelegateWrapper(key?.asName(), default, inherited) { it.boolean } fun VisualObject.number(default: Number? = null, key: String? = null, inherited: Boolean = false) = - DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.number } + DisplayObjectDelegateWrapper(key?.asName(), default, inherited) { it.number } fun VisualObject.double(default: Double? = null, key: String? = null, inherited: Boolean = false) = - DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.double } + DisplayObjectDelegateWrapper(key?.asName(), default, inherited) { it.double } fun VisualObject.int(default: Int? = null, key: String? = null, inherited: Boolean = false) = - DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.int } + DisplayObjectDelegateWrapper(key?.asName(), default, inherited) { it.int } fun VisualObject.node(key: String? = null, inherited: Boolean = true) = - DisplayObjectDelegateWrapper(key?.toName(), null, inherited) { it.node } + DisplayObjectDelegateWrapper(key?.asName(), null, inherited) { it.node } fun VisualObject.item(key: String? = null, inherited: Boolean = true) = - DisplayObjectDelegateWrapper(key?.toName(), null, inherited) { it } + DisplayObjectDelegateWrapper(key?.asName(), null, inherited) { it } //fun Configurable.spec(spec: Specification, key: String? = null) = ChildConfigDelegate(key) { spec.wrap(this) } @JvmName("safeString") fun VisualObject.string(default: String, key: String? = null, inherited: Boolean = false) = - DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.string } + DisplayObjectDelegateWrapper(key?.asName(), default, inherited) { it.string } @JvmName("safeBoolean") fun VisualObject.boolean(default: Boolean, key: String? = null, inherited: Boolean = false) = - DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.boolean } + DisplayObjectDelegateWrapper(key?.asName(), default, inherited) { it.boolean } @JvmName("safeNumber") fun VisualObject.number(default: Number, key: String? = null, inherited: Boolean = false) = - DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.number } + DisplayObjectDelegateWrapper(key?.asName(), default, inherited) { it.number } @JvmName("safeDouble") fun VisualObject.double(default: Double, key: String? = null, inherited: Boolean = false) = - DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.double } + DisplayObjectDelegateWrapper(key?.asName(), default, inherited) { it.double } @JvmName("safeInt") fun VisualObject.int(default: Int, key: String? = null, inherited: Boolean = false) = - DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.int } + DisplayObjectDelegateWrapper(key?.asName(), default, inherited) { it.int } + inline fun > VisualObject.enum(default: E, key: String? = null, inherited: Boolean = false) = - DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { item -> item.string?.let { enumValueOf(it) } } + DisplayObjectDelegateWrapper(key?.let{ NameToken(it).asName()}, default, inherited) { item -> item.string?.let { enumValueOf(it) } } //merge properties @@ -116,11 +120,11 @@ fun VisualObject.merge( ): ReadOnlyProperty { return object : ReadOnlyProperty { override fun getValue(thisRef: VisualObject, property: KProperty<*>): T { - val name = key?.toName() ?: property.name.toName() + val name = key?.asName() ?: property.name.asName() val sequence = sequence> { var thisObj: VisualObject? = thisRef while (thisObj != null) { - thisObj.properties[name]?.let { yield(it) } + thisObj.config[name]?.let { yield(it) } thisObj = thisObj.parent } } diff --git a/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/DisplayObjectFXListener.kt b/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/DisplayObjectFXListener.kt index 1fe12696..62bcaffd 100644 --- a/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/DisplayObjectFXListener.kt +++ b/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/DisplayObjectFXListener.kt @@ -4,7 +4,6 @@ import hep.dataforge.meta.* import hep.dataforge.names.Name import hep.dataforge.names.toName import hep.dataforge.vis.common.VisualObject -import hep.dataforge.vis.common.getProperty import hep.dataforge.vis.common.onChange import javafx.beans.binding.ObjectBinding import tornadofx.* @@ -24,7 +23,7 @@ class DisplayObjectFXListener(val obj: VisualObject) { operator fun get(key: Name): ObjectBinding?> { return binndings.getOrPut(key) { object : ObjectBinding?>() { - override fun computeValue(): MetaItem<*>? = obj.getProperty(key) + override fun computeValue(): MetaItem<*>? = obj.properties[key] } } } diff --git a/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/RendererDemoApp.kt b/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/RendererDemoApp.kt index e0778632..1e165cb8 100644 --- a/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/RendererDemoApp.kt +++ b/dataforge-vis-spatial-fx/src/main/kotlin/hep/dataforge/vis/spatial/RendererDemoApp.kt @@ -27,21 +27,14 @@ class RendererDemoView : View() { renderer.render { group = group { - box { - xSize = 100.0 - ySize = 100.0 - zSize = 100.0 - } - box { + box(100,100,100) + box(100,100,100) { x = 110.0 - xSize = 100.0 - ySize = 100.0 - zSize = 100.0 } } } - var color by group.properties.number(1530).int + var color by group.config.number(1530).int GlobalScope.launch { val random = Random(111) diff --git a/dataforge-vis-spatial-gdml/src/commonMain/kotlin/hep/dataforge/vis/spatial/gdml/visualGDML.kt b/dataforge-vis-spatial-gdml/src/commonMain/kotlin/hep/dataforge/vis/spatial/gdml/visualGDML.kt index 43ac52b6..2951dbff 100644 --- a/dataforge-vis-spatial-gdml/src/commonMain/kotlin/hep/dataforge/vis/spatial/gdml/visualGDML.kt +++ b/dataforge-vis-spatial-gdml/src/commonMain/kotlin/hep/dataforge/vis/spatial/gdml/visualGDML.kt @@ -11,28 +11,57 @@ import kotlin.math.cos import kotlin.math.sin -private fun VisualObject.applyPosition(pos: GDMLPosition): VisualObject = apply { - x = pos.x - y = pos.y - z = pos.z - //TODO convert units if needed -} +private fun VisualObject.withPosition( + pos: GDMLPosition? = null, + rotation: GDMLRotation? = null, + scale: GDMLScale? = null +): VisualObject = + apply { + // if( this is VisualObject3D){ +// pos?.let { +// x = pos.x +// y = pos.y +// z = pos.z +// } +// rotation?.let { +// rotationX = rotation.x +// rotationY = rotation.y +// rotationZ = rotation.z +// } +// } else { + pos?.let { + x = pos.x + y = pos.y + z = pos.z + } + rotation?.let { + rotationX = rotation.x + rotationY = rotation.y + rotationZ = rotation.z + } + //} + scale?.let { + scaleX = scale.x + scaleY = scale.y + scaleZ = scale.z + } + //TODO convert units if needed + } -private fun VisualObject.applyRotation(rotation: GDMLRotation): VisualObject = apply { - rotationX = rotation.x - rotationY = rotation.y - rotationZ = rotation.z - //TODO convert units if needed -} -private fun VisualGroup.addSolid(root: GDML, solid: GDMLSolid, block: VisualObject.() -> Unit = {}): VisualObject { +private fun VisualGroup.addSolid( + root: GDML, + solid: GDMLSolid, + name: String? = null, + block: VisualObject.() -> Unit = {} +): VisualObject { return when (solid) { - is GDMLBox -> box(solid.x, solid.y, solid.z) - is GDMLTube -> cylinder(solid.rmax, solid.z) { + is GDMLBox -> box(solid.x, solid.y, solid.z, name) + is GDMLTube -> cylinder(solid.rmax, solid.z, name) { startAngle = solid.startphi angle = solid.deltaphi } - is GDMLXtru -> extrude { + is GDMLXtru -> extrude(name) { shape { solid.vertices.forEach { point(it.x, it.y) @@ -46,6 +75,7 @@ private fun VisualGroup.addSolid(root: GDML, solid: GDMLSolid, block: VisualObje //Add solid with modified scale val innerSolid = solid.solidref.resolve(root) ?: error("Solid with tag ${solid.solidref.ref} for scaled solid ${solid.name} not defined") + addSolid(root, innerSolid) { block() scaleX = scaleX.toDouble() * solid.scale.x.toDouble() @@ -53,12 +83,12 @@ private fun VisualGroup.addSolid(root: GDML, solid: GDMLSolid, block: VisualObje scaleZ = scaleZ.toDouble() * solid.scale.z.toDouble() } } - is GDMLSphere -> sphere(solid.rmax, solid.deltaphi, solid.deltatheta) { + is GDMLSphere -> sphere(solid.rmax, solid.deltaphi, solid.deltatheta, name) { phiStart = solid.startphi.toDouble() thetaStart = solid.starttheta.toDouble() } - is GDMLOrb -> sphere(solid.r) - is GDMLPolyhedra -> extrude { + is GDMLOrb -> sphere(solid.r, name = name) + is GDMLPolyhedra -> extrude(name) { //getting the radius of first require(solid.planes.size > 1) { "The polyhedron geometry requires at least two planes" } val baseRadius = solid.planes.first().rmax.toDouble() @@ -81,14 +111,13 @@ private fun VisualGroup.addSolid(root: GDML, solid: GDMLSolid, block: VisualObje is GDMLSubtraction -> CompositeType.SUBTRACT is GDMLIntersection -> CompositeType.INTERSECT } - return composite(type) { + + return composite(type, name) { addSolid(root, first) { - solid.resolveFirstPosition(root)?.let { applyPosition(it) } - solid.resolveFirstRotation(root)?.let { applyRotation(it) } + withPosition(solid.resolveFirstPosition(root), solid.resolveFirstRotation(root), null) } addSolid(root, second) - solid.resolvePosition(root)?.let { applyPosition(it) } - solid.resolveRotation(root)?.let { applyRotation(it) } + withPosition(solid.resolvePosition(root), solid.resolveRotation(root), null) } } }.apply(block) @@ -97,30 +126,42 @@ private fun VisualGroup.addSolid(root: GDML, solid: GDMLSolid, block: VisualObje private fun VisualGroup.addVolume( root: GDML, group: GDMLGroup, + position: GDMLPosition? = null, + rotation: GDMLRotation? = null, + scale: GDMLScale? = null, resolveColor: GDMLMaterial.() -> Meta -): VisualGroup { - if (group is GDMLVolume) { - val solid = group.solidref.resolve(root) - ?: error("Solid with tag ${group.solidref.ref} for volume ${group.name} not defined") - val material = group.materialref.resolve(root) - ?: error("Material with tag ${group.materialref.ref} for volume ${group.name} not defined") +) { - addSolid(root, solid) { - color(material.resolveColor()) + group(group.name) { + withPosition(position, rotation, scale) + + if (group is GDMLVolume) { + val solid = group.solidref.resolve(root) + ?: error("Solid with tag ${group.solidref.ref} for volume ${group.name} not defined") + val material = group.materialref.resolve(root) + ?: error("Material with tag ${group.materialref.ref} for volume ${group.name} not defined") + + addSolid(root, solid, solid.name) { + color(material.resolveColor()) + } + //TODO render placements } - //TODO render placements - } - group.physVolumes.forEach { - val volume: GDMLGroup = - it.volumeref.resolve(root) ?: error("Volume with ref ${it.volumeref.ref} could not be resolved") - addVolume(root, volume, resolveColor).apply { - it.resolvePosition(root)?.let { pos -> applyPosition(pos) } - it.resolveRotation(root)?.let { rot -> applyRotation(rot) } + group.physVolumes.forEach { physVolume -> + val volume: GDMLGroup = physVolume.volumeref.resolve(root) + ?: error("Volume with ref ${physVolume.volumeref.ref} could not be resolved") + + addVolume( + root, + volume, + physVolume.resolvePosition(root), + physVolume.resolveRotation(root), + physVolume.resolveScale(root), + resolveColor + ) } } - return this } diff --git a/dataforge-vis-spatial-gdml/src/jsMain/kotlin/hep/dataforge/vis/spatial/gdml/demo/GDMLDemoApp.kt b/dataforge-vis-spatial-gdml/src/jsMain/kotlin/hep/dataforge/vis/spatial/gdml/demo/GDMLDemoApp.kt index 328a28fb..1c13315b 100644 --- a/dataforge-vis-spatial-gdml/src/jsMain/kotlin/hep/dataforge/vis/spatial/gdml/demo/GDMLDemoApp.kt +++ b/dataforge-vis-spatial-gdml/src/jsMain/kotlin/hep/dataforge/vis/spatial/gdml/demo/GDMLDemoApp.kt @@ -17,7 +17,7 @@ import scientifik.gdml.GDML import kotlin.browser.document import kotlin.dom.clear -class GDMLDemoApp : ApplicationBase() { +private class GDMLDemoApp : ApplicationBase() { /** @@ -56,13 +56,13 @@ class GDMLDemoApp : ApplicationBase() { val context = Global.context("demo") {} val three = context.plugins.load(ThreePlugin) - val canvas = document.getElementById("canvas") ?: error("Element with id canvas not found on page") - canvas.clear() - val output = three.output(canvas) //val url = URL("https://drive.google.com/open?id=1w5e7fILMN83JGgB8WANJUYm8OW2s0WVO") + val canvas = document.getElementById("canvas") ?: error("Element with id canvas not found on page") val action: suspend (String) -> Unit = { + canvas.clear() + val output = three.output(canvas) val gdml = GDML.format.parse(GDML.serializer(), it) val visual = gdml.toVisual() output.render(visual) diff --git a/dataforge-vis-spatial-gdml/src/jvmTest/kotlin/hep/dataforge/vis/spatial/gdml/BMNTest.kt b/dataforge-vis-spatial-gdml/src/jvmTest/kotlin/hep/dataforge/vis/spatial/gdml/BMNTest.kt index a04efbe5..b98993f5 100644 --- a/dataforge-vis-spatial-gdml/src/jvmTest/kotlin/hep/dataforge/vis/spatial/gdml/BMNTest.kt +++ b/dataforge-vis-spatial-gdml/src/jvmTest/kotlin/hep/dataforge/vis/spatial/gdml/BMNTest.kt @@ -13,7 +13,7 @@ class BMNTest { val url = URL("https://drive.google.com/open?id=1w5e7fILMN83JGgB8WANJUYm8OW2s0WVO") val file = File("D:\\Work\\Projects\\gdml.kt\\src\\commonTest\\resources\\gdml\\geofile_full.xml") - val stream = if(file.exists()){ + val stream = if (file.exists()) { file.inputStream() } else { url.openStream() @@ -21,7 +21,7 @@ class BMNTest { val xmlReader = StAXReader(stream, "UTF-8") val xml = GDML.format.parse(GDML.serializer(), xmlReader) - repeat(5) { + repeat(20) { xml.toVisual() } } diff --git a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/demo/ThreeDemoApp.kt b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/demo/ThreeDemoApp.kt index 3234ade9..41f833da 100644 --- a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/demo/ThreeDemoApp.kt +++ b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/demo/ThreeDemoApp.kt @@ -17,7 +17,7 @@ import kotlin.math.sin import kotlin.random.Random -class ThreeDemoApp : ApplicationBase() { +private class ThreeDemoApp : ApplicationBase() { override val stateKeys: List = emptyList() @@ -39,18 +39,12 @@ class ThreeDemoApp : ApplicationBase() { demo("dynamic", "Dynamic properties") { val group = group { - box { + box(100, 100, 100) { z = 110.0 - xSize = 100.0 - ySize = 100.0 - zSize = 100.0 } - box { + box(100, 100, 100) { visible = false x = 110.0 - xSize = 100.0 - ySize = 100.0 - zSize = 100.0 //override color for this cube color(1530) @@ -63,7 +57,7 @@ class ThreeDemoApp : ApplicationBase() { } } - var material by group.properties.number(1530).int + var material by group.config.number(1530).int GlobalScope.launch { val random = Random(111) @@ -100,34 +94,28 @@ class ThreeDemoApp : ApplicationBase() { demo("CSG", "CSG operations") { composite(CompositeType.UNION) { box(100, 100, 100) { - z = 100 - rotationX = PI / 4 - rotationY = PI / 4 + z = 50 } - box(100, 100, 100) + sphere(50) color { "color" to Colors.lightgreen "opacity" to 0.3 } } composite(CompositeType.INTERSECT) { - box(100, 100, 100) { - z = 100 - rotationX = PI / 4 - rotationY = PI / 4 - } - box(100, 100, 100) y = 300 + box(100, 100, 100) { + z = 50 + } + sphere(50) color(Colors.red) } composite(CompositeType.SUBTRACT) { - box(100, 100, 100) { - z = 100 - rotationX = PI / 4 - rotationY = PI / 4 - } - box(100, 100, 100) y = -300 + box(100, 100, 100) { + z = 50 + } + sphere(50) color(Colors.blue) } } diff --git a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/three/ThreeCylinderFactory.kt b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/three/ThreeCylinderFactory.kt index 756d2116..003c6a29 100644 --- a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/three/ThreeCylinderFactory.kt +++ b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/three/ThreeCylinderFactory.kt @@ -11,9 +11,9 @@ object ThreeCylinderFactory : MeshThreeFactory(Cylinder::class) { return obj.detail?.let { val segments = it.toDouble().pow(0.5).toInt() CylinderBufferGeometry( - radiusTop = obj.upperRadius!!, - radiusBottom = obj.radius!!, - height = obj.height!!, + radiusTop = obj.upperRadius, + radiusBottom = obj.radius, + height = obj.height, radialSegments = segments, heightSegments = segments, openEnded = false, @@ -21,9 +21,9 @@ object ThreeCylinderFactory : MeshThreeFactory(Cylinder::class) { thetaLength = obj.angle ) } ?: CylinderBufferGeometry( - radiusTop = obj.upperRadius!!, - radiusBottom = obj.radius!!, - height = obj.height!!, + radiusTop = obj.upperRadius, + radiusBottom = obj.radius, + height = obj.height, openEnded = false, thetaStart = obj.startAngle, thetaLength = obj.angle diff --git a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/three/ThreeFactory.kt b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/three/ThreeFactory.kt index 70bd4116..81d1031b 100644 --- a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/three/ThreeFactory.kt +++ b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/three/ThreeFactory.kt @@ -1,11 +1,10 @@ package hep.dataforge.vis.spatial.three import hep.dataforge.meta.boolean +import hep.dataforge.meta.get import hep.dataforge.names.startsWith -import hep.dataforge.names.toName import hep.dataforge.provider.Type import hep.dataforge.vis.common.VisualObject -import hep.dataforge.vis.common.getProperty import hep.dataforge.vis.common.onChange import hep.dataforge.vis.spatial.* import hep.dataforge.vis.spatial.three.ThreeFactory.Companion.TYPE @@ -19,7 +18,7 @@ import info.laht.threekt.objects.LineSegments import info.laht.threekt.objects.Mesh import kotlin.reflect.KClass -internal val VisualObject.material get() = getProperty("material").material() +internal val VisualObject.material get() = properties["material"].material() /** * Builder and updater for three.js object @@ -43,14 +42,14 @@ interface ThreeFactory { val mesh = Mesh(geometry, obj.material) //inherited edges definition, enabled by default - if (obj.getProperty("edges.enabled").boolean != false) { - val material = obj.getProperty("edges.material")?.material() ?: Materials.DEFAULT + if (obj.properties["edges.enabled"].boolean != false) { + val material = obj.properties["edges.material"]?.material() ?: Materials.DEFAULT mesh.add(LineSegments(EdgesGeometry(mesh.geometry as BufferGeometry), material)) } //inherited wireframe definition, disabled by default - if (obj.getProperty("wireframe.enabled").boolean == true) { - val material = obj.getProperty("edges.material")?.material() ?: Materials.DEFAULT + if (obj.properties["wireframe.enabled"].boolean == true) { + val material = obj.properties["edges.material"]?.material() ?: Materials.DEFAULT mesh.add(LineSegments(WireframeGeometry(mesh.geometry as BufferGeometry), material)) } @@ -63,9 +62,9 @@ interface ThreeFactory { //updated material mesh.material = obj.material } else if ( - name.startsWith("pos".toName()) || - name.startsWith("scale".toName()) || - name.startsWith("rotation".toName()) || + name.startsWith(PropertyNames3D.position) || + name.startsWith(PropertyNames3D.rotation) || + name.startsWith(PropertyNames3D.scale) || name.toString() == "visible" ) { //update position of mesh using this object diff --git a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/three/ThreePlugin.kt b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/three/ThreePlugin.kt index 5c46b5d9..3d9a5247 100644 --- a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/three/ThreePlugin.kt +++ b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/three/ThreePlugin.kt @@ -38,7 +38,14 @@ class ThreePlugin : AbstractPlugin() { fun buildObject3D(obj: VisualObject): Object3D { return when (obj) { - is VisualGroup -> Group(obj.map { buildObject3D(it) }).apply { + is VisualGroup -> Group(obj.mapNotNull { + try { + buildObject3D(it) + } catch (ex: Throwable){ + logger.error(ex){"Failed to render $it"} + null + } + }).apply { updatePosition(obj) } is Composite -> compositeFactory(obj) diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Box.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Box.kt index 7f75f5c3..a8200122 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Box.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Box.kt @@ -1,23 +1,18 @@ package hep.dataforge.vis.spatial -import hep.dataforge.meta.EmptyMeta import hep.dataforge.meta.Meta import hep.dataforge.vis.common.VisualGroup -import hep.dataforge.vis.common.DisplayLeaf import hep.dataforge.vis.common.VisualObject -import hep.dataforge.vis.common.double -class Box(parent: VisualObject?, meta: Meta) : DisplayLeaf(parent, meta), Shape { - var xSize by double(100.0) - var ySize by double(100.0) - var zSize by double(100.0) +class Box(parent: VisualObject?, val xSize: Number, val ySize: Number, val zSize: Number, meta: Array) : + VisualObject3D(parent, meta), Shape { //TODO add helper for color configuration override fun toGeometry(geometryBuilder: GeometryBuilder) { - val dx = xSize / 2 - val dy = ySize / 2 - val dz = zSize / 2 + val dx = xSize.toDouble() / 2 + val dy = ySize.toDouble() / 2 + val dz = zSize.toDouble() / 2 val node1 = Point3D(-dx, -dy, -dz) val node2 = Point3D(dx, -dy, -dz) val node3 = Point3D(dx, dy, -dz) @@ -40,12 +35,15 @@ class Box(parent: VisualObject?, meta: Meta) : DisplayLeaf(parent, meta), Shape } } -fun VisualGroup.box(meta: Meta = EmptyMeta, action: Box.() -> Unit = {}) = - Box(this, meta).apply(action).also { add(it) } +//fun VisualGroup.box(meta: Meta = EmptyMeta, action: Box.() -> Unit = {}) = +// Box(this, meta).apply(action).also { add(it) } -fun VisualGroup.box(xSize: Number, ySize: Number, zSize: Number, meta: Meta = EmptyMeta, action: Box.() -> Unit = {}) = - Box(this, meta).apply(action).apply{ - this.xSize = xSize.toDouble() - this.ySize = ySize.toDouble() - this.zSize = zSize.toDouble() - }.also { add(it) } \ No newline at end of file +fun VisualGroup.box( + xSize: Number, + ySize: Number, + zSize: Number, + name: String? = null, + vararg meta: Meta, + action: Box.() -> Unit = {} +) = + Box(this, xSize, ySize, zSize, meta).apply(action).also { set(name, it) } \ No newline at end of file diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Composite.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Composite.kt index ac987e1d..0123e1df 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Composite.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Composite.kt @@ -1,9 +1,8 @@ package hep.dataforge.vis.spatial -import hep.dataforge.meta.EmptyMeta import hep.dataforge.meta.Meta import hep.dataforge.meta.seal -import hep.dataforge.vis.common.DisplayLeaf +import hep.dataforge.meta.update import hep.dataforge.vis.common.VisualGroup import hep.dataforge.vis.common.VisualObject @@ -13,28 +12,35 @@ enum class CompositeType { SUBTRACT } -class Composite( +open class Composite( parent: VisualObject?, val first: VisualObject, val second: VisualObject, val type: CompositeType = CompositeType.UNION, - meta: Meta = EmptyMeta -) : DisplayLeaf(parent, meta) + meta: Array +) : VisualObject3D(parent, meta) -fun VisualGroup.composite(type: CompositeType, builder: VisualGroup.() -> Unit): Composite { +fun VisualGroup.composite( + type: CompositeType, + name: String? = null, + vararg meta: Meta, + builder: VisualGroup.() -> Unit +): Composite { val group = VisualGroup().apply(builder) val children = group.toList() if (children.size != 2) error("Composite requires exactly two children") - return Composite(this, children[0], children[1], type, group.properties.seal()).also { - this.add(it) + val groupMeta = group.properties.seal() + return Composite(this, children[0], children[1], type, meta).also { + it.config.update(groupMeta) + set(name, it) } } -fun VisualGroup.union(builder: VisualGroup.() -> Unit) = - composite(CompositeType.UNION,builder) +fun VisualGroup.union(name: String? = null, vararg meta: Meta, builder: VisualGroup.() -> Unit) = + composite(CompositeType.UNION, name, *meta, builder = builder) -fun VisualGroup.subtract(builder: VisualGroup.() -> Unit) = - composite(CompositeType.SUBTRACT,builder) +fun VisualGroup.subtract(name: String? = null, vararg meta: Meta, builder: VisualGroup.() -> Unit) = + composite(CompositeType.SUBTRACT, name, *meta, builder = builder) -fun VisualGroup.intersect(builder: VisualGroup.() -> Unit) = - composite(CompositeType.INTERSECT,builder) \ No newline at end of file +fun VisualGroup.intersect(name: String? = null, vararg meta: Meta, builder: VisualGroup.() -> Unit) = + composite(CompositeType.INTERSECT, name, *meta, builder = builder) \ No newline at end of file diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Convex.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Convex.kt index 55426f82..35cadcd6 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Convex.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Convex.kt @@ -1,26 +1,19 @@ package hep.dataforge.vis.spatial -import hep.dataforge.meta.* +import hep.dataforge.meta.Meta import hep.dataforge.vis.common.VisualGroup -import hep.dataforge.vis.common.DisplayLeaf +import hep.dataforge.vis.common.VisualLeaf import hep.dataforge.vis.common.VisualObject -class Convex(parent: VisualObject?, meta: Meta) : DisplayLeaf(parent, meta) { +class Convex(parent: VisualObject?, val points: List, meta: Array) : VisualLeaf(parent, meta) { - val points = points(properties["points"] ?: error("Vertices not defined")) companion object { const val TYPE = "geometry.3d.convex" - - fun points(item: MetaItem<*>): List { - return item.node?.getAll("point")?.map { (_, value) -> - Point3D.from(value.node?: error("Point definition is not a node")) - } ?: emptyList() - } } } -fun VisualGroup.convex(meta: Meta = EmptyMeta, action: ConvexBuilder.() -> Unit = {}) = +fun VisualGroup.convex(vararg meta: Meta, action: ConvexBuilder.() -> Unit = {}) = ConvexBuilder().apply(action).build(this, meta).also { add(it) } class ConvexBuilder { @@ -30,13 +23,7 @@ class ConvexBuilder { points.add(Point3D(x, y, z)) } - fun build(parent: VisualObject?, meta: Meta): Convex { - val points = buildMeta { - points.forEachIndexed { index, value -> - "points.point[$index]" to value.toMeta() - } - }.seal() - - return Convex(parent, points) + fun build(parent: VisualObject?, meta: Array): Convex { + return Convex(parent, points, meta) } } \ No newline at end of file diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Cylinder.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Cylinder.kt index 8e04be1d..be539ba6 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Cylinder.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Cylinder.kt @@ -1,9 +1,8 @@ package hep.dataforge.vis.spatial -import hep.dataforge.meta.EmptyMeta import hep.dataforge.meta.Meta -import hep.dataforge.vis.common.DisplayLeaf import hep.dataforge.vis.common.VisualGroup +import hep.dataforge.vis.common.VisualLeaf import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.common.number import kotlin.math.PI @@ -11,18 +10,23 @@ import kotlin.math.PI /** * A cylinder or cut cone segment */ -class Cylinder(parent: VisualObject?, meta: Meta) : DisplayLeaf(parent, meta) { - var radius by number() - var upperRadius by number(default = radius) - var height by number() +class Cylinder(parent: VisualObject?, radius: Number, height: Number, meta: Array) : + VisualLeaf(parent, meta) { + var radius by number(radius) + var upperRadius by number(radius) + var height by number(height) var startAngle by number(0.0) var angle by number(2 * PI) } -fun VisualGroup.cylinder(r: Number, height: Number, meta: Meta = EmptyMeta, block: Cylinder.() -> Unit = {}): Cylinder { - val cylinder = Cylinder(this, meta) - cylinder.radius = r - cylinder.height = height +fun VisualGroup.cylinder( + r: Number, + height: Number, + name: String? = null, + vararg meta: Meta, + block: Cylinder.() -> Unit = {} +): Cylinder { + val cylinder = Cylinder(this, r, height, meta) cylinder.apply(block) - return cylinder.also { add(it) } + return cylinder.also { set(name, it) } } \ No newline at end of file diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Extruded.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Extruded.kt index 0f1763bb..49dac4ff 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Extruded.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Extruded.kt @@ -1,8 +1,8 @@ package hep.dataforge.vis.spatial -import hep.dataforge.meta.* -import hep.dataforge.vis.common.DisplayLeaf +import hep.dataforge.meta.Meta import hep.dataforge.vis.common.VisualGroup +import hep.dataforge.vis.common.VisualLeaf import hep.dataforge.vis.common.VisualObject import kotlin.math.PI import kotlin.math.cos @@ -18,7 +18,7 @@ class Shape2DBuilder { list.add(Point2D(x, y)) } - infix fun Number.to(y:Number) = point(this, y) + infix fun Number.to(y: Number) = point(this, y) fun build(): Shape2D = list } @@ -31,45 +31,22 @@ fun Shape2DBuilder.polygon(vertices: Int, radius: Number) { } } -class Layer(override val config: Config) : Specific { - var z by number(0.0) - var x by number(0.0) - var y by number(0.0) - var scale by number(1.0) +data class Layer(var x: Number, var y: Number, var z: Number, var scale: Number) - companion object : Specification { - override fun wrap(config: Config): Layer = Layer(config) - } -} +class Extruded(parent: VisualObject?, meta: Array) : VisualLeaf(parent, meta), Shape { -//class Layer(val z: Number, val x: Number = 0.0, val y: Number = 0.0, val scale: Number = 1.0) - -class Extruded(parent: VisualObject?, meta: Meta) : DisplayLeaf(parent, meta), Shape { - - val shape - get() = properties.getAll("shape.point").map { (_, value) -> - Point2D.from(value.node ?: error("Point definition is not a node")) - } + var shape: List = ArrayList() fun shape(block: Shape2DBuilder.() -> Unit) { - val points = Shape2DBuilder().apply(block).build().map { it.toMeta() } - properties["shape.point"] = points + this.shape = Shape2DBuilder().apply(block).build() + //TODO send invalidation signal } - val layers - get() = properties.getAll("layer").values.map { - Layer.wrap(it.node ?: error("layer item is not a node")) - } + val layers: MutableList = ArrayList() - fun layer(z: Number, x: Number = 0.0, y: Number = 0.0, scale: Number = 1.0): Layer { - val layer = Layer.build { - this.x = x - this.y = y - this.z = z - this.scale = scale - } - properties.append("layer", layer) - return layer + fun layer(z: Number, x: Number = 0.0, y: Number = 0.0, scale: Number = 1.0) { + layers.add(Layer(x,y,z,scale)) + //TODO send invalidation signal } override fun toGeometry(geometryBuilder: GeometryBuilder) { @@ -121,5 +98,5 @@ class Extruded(parent: VisualObject?, meta: Meta) : DisplayLeaf(parent, meta), S } } -fun VisualGroup.extrude(meta: Meta = EmptyMeta, action: Extruded.() -> Unit = {}) = - Extruded(this, meta).apply(action).also { add(it) } \ No newline at end of file +fun VisualGroup.extrude(name: String? = null, vararg meta: Meta, action: Extruded.() -> Unit = {}) = + Extruded(this, meta).apply(action).also { set(name, it) } \ No newline at end of file diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Sphere.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Sphere.kt index 501a5881..cdc8e0b9 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Sphere.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Sphere.kt @@ -1,14 +1,13 @@ package hep.dataforge.vis.spatial -import hep.dataforge.meta.EmptyMeta import hep.dataforge.meta.Meta import hep.dataforge.vis.common.VisualGroup -import hep.dataforge.vis.common.DisplayLeaf +import hep.dataforge.vis.common.VisualLeaf import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.common.double import kotlin.math.PI -class Sphere(parent: VisualObject?, meta: Meta) : DisplayLeaf(parent, meta) { +class Sphere(parent: VisualObject?, meta: Array) : VisualLeaf(parent, meta) { var radius by double(50.0) var phiStart by double(0.0) var phi by double(2 * PI) @@ -16,17 +15,15 @@ class Sphere(parent: VisualObject?, meta: Meta) : DisplayLeaf(parent, meta) { var theta by double(PI) } -fun VisualGroup.sphere(meta: Meta = EmptyMeta, action: Sphere.() -> Unit = {}) = - Sphere(this, meta).apply(action).also { add(it) } - fun VisualGroup.sphere( radius: Number, phi: Number = 2 * PI, theta: Number = PI, - meta: Meta = EmptyMeta, + name: String? = null, + vararg meta: Meta, action: Sphere.() -> Unit = {} ) = Sphere(this, meta).apply(action).apply { this.radius = radius.toDouble() this.phi = phi.toDouble() this.theta = theta.toDouble() -}.also { add(it) } \ No newline at end of file +}.also { set(name, it) } \ No newline at end of file diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/VisualObject3D.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/VisualObject3D.kt new file mode 100644 index 00000000..306a820c --- /dev/null +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/VisualObject3D.kt @@ -0,0 +1,196 @@ +package hep.dataforge.vis.spatial + +import hep.dataforge.meta.* +import hep.dataforge.names.plus +import hep.dataforge.output.Output +import hep.dataforge.vis.common.VisualGroup +import hep.dataforge.vis.common.VisualLeaf +import hep.dataforge.vis.common.VisualObject +import hep.dataforge.vis.common.asName + +/** + * Performance optimized version of visual object + */ +open class VisualObject3D(parent: VisualObject?, tagRefs: Array) : VisualLeaf(parent, tagRefs) { + var x: Number? = null; get() = field ?: (this as VisualLeaf).x + var y: Number? = null; get() = field ?: (this as VisualLeaf).y + var z: Number? = null; get() = field ?: (this as VisualLeaf).z + + var rotationX: Number? = null; get() = field ?: (this as VisualLeaf).rotationX + var rotationY: Number? = null; get() = field ?: (this as VisualLeaf).rotationY + var rotationZ: Number? = null; get() = field ?: (this as VisualLeaf).rotationZ +} + +fun VisualGroup.group(key: String? = null, vararg meta: Meta, action: VisualGroup.() -> Unit = {}): VisualGroup = + VisualGroup(this, meta).apply(action).also { set(key, it) } + + +fun Output.render(meta: Meta = EmptyMeta, action: VisualGroup.() -> Unit) = + render(VisualGroup().apply(action), meta) + +//TODO replace properties by containers? + +object PropertyNames3D { + val x = "x".asName() + val y = "y".asName() + val z = "z".asName() + + val position = "pos".asName() + + val xPos = position + x + val yPos = position + y + val zPos = position + z + + val rotation = "rotation".asName() + + val xRotation = rotation + x + val yRotation = rotation + y + val zRotation = rotation + z + + val rotationOrder = rotation + "order" + + val scale = "scale".asName() + + val xScale = scale + x + val yScale = scale + y + val zScale = scale + z +} + +// Common properties + +/** + * Visibility property. Inherited from parent + */ +var VisualObject.visible + get() = properties["visible"].boolean ?: true + set(value) { + config["visible"] = value + } + +// 3D Object position + +/** + * x position property relative to parent. Not inherited + */ +var VisualObject.x + get() = config[PropertyNames3D.xPos].number ?: 0.0 + set(value) { + config[PropertyNames3D.xPos] = value + } + + +/** + * y position property. Not inherited + */ +var VisualObject.y + get() = config[PropertyNames3D.yPos].number ?: 0.0 + set(value) { + config[PropertyNames3D.yPos] = value + } + + +/** + * z position property. Not inherited + */ +var VisualObject.z + get() = config[PropertyNames3D.zPos].number ?: 0.0 + set(value) { + config[PropertyNames3D.zPos] = value + } + +// 3D Object rotation + + +/** + * x rotation relative to parent. Not inherited + */ +var VisualObject.rotationX + get() = config[PropertyNames3D.xRotation].number ?: 0.0 + set(value) { + config[PropertyNames3D.xRotation] = value + } + +/** + * y rotation relative to parent. Not inherited + */ +var VisualObject.rotationY + get() = config[PropertyNames3D.yRotation].number ?: 0.0 + set(value) { + config[PropertyNames3D.yRotation] = value + } + +/** + * z rotation relative to parent. Not inherited + */ +var VisualObject.rotationZ + get() = config[PropertyNames3D.zRotation].number ?: 0.0 + set(value) { + config[PropertyNames3D.zRotation] = value + } + +enum class RotationOrder { + XYZ, + YZX, + ZXY, + XZY, + YXZ, + ZYX +} + +/** + * Rotation order. Not inherited + */ +var VisualObject.rotationOrder: RotationOrder + get() = config[PropertyNames3D.rotationOrder].enum() ?: RotationOrder.XYZ + set(value) { + config[PropertyNames3D.rotationOrder] = value + } + +// 3D object scale + +/** + * X scale. Not inherited + */ +var VisualObject.scaleX + get() = config[PropertyNames3D.xScale].number ?: 1.0 + set(value) { + config[PropertyNames3D.xScale] = value + } + +/** + * Y scale. Not inherited + */ +var VisualObject.scaleY + get() = config[PropertyNames3D.yScale].number ?: 1.0 + set(value) { + config[PropertyNames3D.yScale] = value + } + +/** + * Z scale. Not inherited + */ +var VisualObject.scaleZ + get() = config[PropertyNames3D.zScale].number ?: 1.0 + set(value) { + config[PropertyNames3D.zScale] = value + } + +//TODO add inherited scale + +/** + * Preferred number of polygons for displaying the object. If not defined, uses shape or renderer default + */ +var VisualObject.detail: Int? + get() = properties["detail"]?.int + set(value) { + config["detail"] = value + } + +object World { + const val CAMERA_INITIAL_DISTANCE = -500.0 + const val CAMERA_INITIAL_X_ANGLE = -50.0 + const val CAMERA_INITIAL_Y_ANGLE = 0.0 + const val CAMERA_INITIAL_Z_ANGLE = -210.0 + const val CAMERA_NEAR_CLIP = 0.1 + const val CAMERA_FAR_CLIP = 10000.0 +} \ No newline at end of file diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/displayObject3D.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/displayObject3D.kt deleted file mode 100644 index 3d1ebfdd..00000000 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/displayObject3D.kt +++ /dev/null @@ -1,164 +0,0 @@ -package hep.dataforge.vis.spatial - -import hep.dataforge.meta.* -import hep.dataforge.names.toName -import hep.dataforge.output.Output -import hep.dataforge.vis.common.VisualGroup -import hep.dataforge.vis.common.VisualObject -import hep.dataforge.vis.common.getProperty - -fun VisualGroup.group(meta: Meta = EmptyMeta, action: VisualGroup.() -> Unit = {}): VisualGroup = - VisualGroup(this, meta).apply(action).also { add(it) } - - -fun Output.render(meta: Meta = EmptyMeta, action: VisualGroup.() -> Unit) = - render(VisualGroup(null, EmptyMeta).apply(action), meta) - -//TODO replace properties by containers? - -// Common properties - -/** - * Visibility property. Inherited from parent - */ -var VisualObject.visible - get() = getProperty("visible").boolean ?: true - set(value) { - properties["visible"] = value - } - -// 3D Object position - -private val xPos = "pos.x".toName() -/** - * x position property relative to parent. Not inherited - */ -var VisualObject.x - get() = properties[xPos].number ?: 0.0 - set(value) { - properties[xPos] = value - } - -private val yPos = "pos.y".toName() - -/** - * y position property. Not inherited - */ -var VisualObject.y - get() = properties[yPos].number ?: 0.0 - set(value) { - properties[yPos] = value - } - -private val zPos = "pos.z".toName() - -/** - * z position property. Not inherited - */ -var VisualObject.z - get() = properties[zPos].number ?: 0.0 - set(value) { - properties[zPos] = value - } - -// 3D Object rotation - -private val xRotation = "rotation.x".toName() - -/** - * x rotation relative to parent. Not inherited - */ -var VisualObject.rotationX - get() = properties[xRotation].number ?: 0.0 - set(value) { - properties[xRotation] = value - } - -private val yRotation = "rotation.y".toName() - -/** - * y rotation relative to parent. Not inherited - */ -var VisualObject.rotationY - get() = properties[yRotation].number ?: 0.0 - set(value) { - properties[yRotation] = value - } - -private val zRotation = "rotation.z".toName() - -/** - * z rotation relative to parent. Not inherited - */ -var VisualObject.rotationZ - get() = properties[zRotation].number ?: 0.0 - set(value) { - properties[zRotation] = value - } - -enum class RotationOrder { - XYZ, - YZX, - ZXY, - XZY, - YXZ, - ZYX -} - -/** - * Rotation order. Not inherited - */ -var VisualObject.rotationOrder: RotationOrder - get() = getProperty("rotation.order").enum() ?: RotationOrder.XYZ - set(value) { - properties["rotation.order"] = value - } - -// 3D object scale - -/** - * X scale. Not inherited - */ -var VisualObject.scaleX - get() = properties["scale.x"].number ?: 1.0 - set(value) { - properties["scale.x"] = value - } - -/** - * Y scale. Not inherited - */ -var VisualObject.scaleY - get() = properties["scale.y"].number ?: 1.0 - set(value) { - properties["scale.y"] = value - } - -/** - * Z scale. Not inherited - */ -var VisualObject.scaleZ - get() = properties["scale.z"].number ?: 1.0 - set(value) { - properties["scale.z"] = value - } - -//TODO add inherited scale - -/** - * Preferred number of polygons for displaying the object. If not defined, uses shape or renderer default - */ -var VisualObject.detail: Int? - get() = properties["detail"]?.int - set(value) { - properties["detail"] = value - } - -object World { - const val CAMERA_INITIAL_DISTANCE = -500.0 - const val CAMERA_INITIAL_X_ANGLE = -50.0 - const val CAMERA_INITIAL_Y_ANGLE = 0.0 - const val CAMERA_INITIAL_Z_ANGLE = -210.0 - const val CAMERA_NEAR_CLIP = 0.1 - const val CAMERA_FAR_CLIP = 10000.0 -} \ No newline at end of file diff --git a/dataforge-vis-spatial/src/commonTest/kotlin/hep/dataforge/vis/spatial/ConvexTest.kt b/dataforge-vis-spatial/src/commonTest/kotlin/hep/dataforge/vis/spatial/ConvexTest.kt index 48a93aa6..85291506 100644 --- a/dataforge-vis-spatial/src/commonTest/kotlin/hep/dataforge/vis/spatial/ConvexTest.kt +++ b/dataforge-vis-spatial/src/commonTest/kotlin/hep/dataforge/vis/spatial/ConvexTest.kt @@ -26,7 +26,7 @@ class ConvexTest { val convex = group.first() as Convex - val pointsNode = convex.properties["points"].node + val pointsNode = convex.config["points"].node assertEquals(8, pointsNode?.items?.count()) val points = pointsNode?.getAll("point".toName()) diff --git a/dataforge-vis-spatial/src/commonTest/kotlin/hep/dataforge/vis/spatial/GroupTest.kt b/dataforge-vis-spatial/src/commonTest/kotlin/hep/dataforge/vis/spatial/GroupTest.kt new file mode 100644 index 00000000..c9f2ea51 --- /dev/null +++ b/dataforge-vis-spatial/src/commonTest/kotlin/hep/dataforge/vis/spatial/GroupTest.kt @@ -0,0 +1,52 @@ +package hep.dataforge.vis.spatial + +import hep.dataforge.vis.common.Colors +import hep.dataforge.vis.common.VisualGroup +import hep.dataforge.vis.common.color +import kotlin.math.PI +import kotlin.test.Test +import kotlin.test.assertEquals + +class GroupTest { + @Test + fun testGroupWithComposite(){ + val group = VisualGroup().apply{ + union { + box(100, 100, 100) { + z = 100 + rotationX = PI / 4 + rotationY = PI / 4 + } + box(100, 100, 100) + color { + "color" to Colors.lightgreen + "opacity" to 0.3 + } + } + intersect{ + box(100, 100, 100) { + z = 100 + rotationX = PI / 4 + rotationY = PI / 4 + } + box(100, 100, 100) + y = 300 + color(Colors.red) + } + subtract{ + box(100, 100, 100) { + z = 100 + rotationX = PI / 4 + rotationY = PI / 4 + } + box(100, 100, 100) + y = -300 + color(Colors.blue) + } + } + + assertEquals(3, group.count()) + assertEquals(300.0,group.toList()[1].y.toDouble()) + assertEquals(-300.0,group.toList()[2].y.toDouble()) + } +} \ No newline at end of file