diff --git a/README.md b/README.md index afdbe49e..51eba421 100644 --- a/README.md +++ b/README.md @@ -1,41 +1,99 @@ [![JetBrains Research](https://jb.gg/badges/research.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub) -# DataForge Visualisation Platform +# DataForge Visualization Platform -This repository contains [DataForge](http://npm.mipt.ru/dataforge/) -(also [here](https://github.com/mipt-npm/dataforge-core)) components useful for visualization in -various scientific applications. The main application for now is 3D visualization for particle -physics experiments. Other applications including 2D plots are planned for future. +## Table of contents -The project is developed as a Kotlin multiplatform application, currently -targeting browser JavaScript and JVM. +* [Introduction](#introduction) +* [Features](#features) +* [About DataForge](#about-dataforge) +* [Modules contained in this repository](#modules-contained-in-this-repository) + * [dataforge-vis-common](#dataforge-vis-common) + * [dataforge-vis-spatial](#dataforge-vis-spatial) + * [dataforge-vis-spatial-gdml](#dataforge-vis-spatial-gdml) + * [dataforge-vis-jsroot](#dataforge-vis-jsroot) +* [Demonstrations](#demonstrations) + * [Spatial Showcase](#spatial-showcase) + * [Muon Monitor](#muon-monitor-visualization) + * [GDML Example](#gdml-example) -Main features: + +## Introduction + +This repository contains a [DataForge](#about-dataforge)\-based framework +used for visualization in various scientific applications. + +The main framework's use case for now is 3D visualization for particle physics experiments. +Other applications including 2D plots are planned for the future. + +The project is being developed as a Kotlin multiplatform application, currently targeting browser +JavaScript and JVM. + + +## Features + +The main framework's features for now include: - 3D visualization of complex experimental set-ups - Event display such as particle tracks, etc. - Scales up to few hundred thousands of elements - Camera move, rotate, zoom-in and zoom-out -- Object tree with property editor +- Scene graph as an object tree with property editor - Settings export and import - Multiple platform support - -## Modules contained in this repository: +## About DataForge + +DataForge is a software framework for automated scientific data processing. DataForge Visualization +Platform uses some of the concepts and modules of DataForge, including: `Meta`, `Configuration`, `Context`, +`Provider`, and some others. + +To learn more about DataForge, please consult the following URLs: + * [Kotlin multiplatform implementation of DataForge](https://github.com/mipt-npm/dataforge-core) + * [DataForge documentation](http://npm.mipt.ru/dataforge/) + * [Original implementation of DataForge](https://bitbucket.org/Altavir/dataforge/src/default/) + + +## Modules contained in this repository ### dataforge-vis-common -Common visualisation objects such as VisualObject and VisualGroup. +Contains a general hierarchy of classes and interfaces useful for visualization. +This module is not specific to 3D-visualization. + +The `dataforge-vis-common` module also includes configuration editors for JS (in `jsMain`) and JVM (in `jvmMain`). + +##### Class diagram: + +![](doc/resources/class-diag-common.png) ### dataforge-vis-spatial -Includes common description and serializers for 3D visualisation, JavaFX and Three.js implementations. +Includes common classes and serializers for 3D visualization, Three.js and JavaFX implementations. + +##### Class diagram: + +![](doc/resources/class-diag-3d.png) + +##### Prototypes + +One of the important features of the framework is support for 3D object prototypes (sometimes +also referred to as templates). The idea is that prototype geometry can be rendered once and reused +for multiple objects. This helps to significantly decrease memory usage. + +The `prototypes` property tree is defined in `VisualGroup3D` class, and `Proxy` class helps to reuse a template object. + +##### Styles + +`VisualGroup3D` has a `styleSheet` property that can optionally define styles at the Group's +level. Styles are applied to child (descendant) objects using `styles: List` property defined +in `VisualObject`. ### dataforge-vis-spatial-gdml -GDML bindings for 3D visualisation (to be moved to gdml project). +GDML bindings for 3D visualization (to be moved to gdml project). ### dataforge-vis-jsroot @@ -45,20 +103,23 @@ Some JSROOT bindings. Note: Currently, this part is experimental and put here for completeness. This module may not build. -### demo +## Demonstrations -Several demonstrations of using the dataforge-vis framework: +The `demo` module contains several demonstrations of using the `dataforge-vis` framework: -##### spatial-showcase +### Spatial Showcase -Contains a simple demonstration (grid with a few shapes that you can rotate, move camera, etc.). +Contains a simple demonstration with a grid including a few shapes that you can rotate, move camera, and so on. +Some shapes will also periodically change their color and visibility. -To see the demo: run `demo/spatial-showcase/distribution/installJsDist` Gradle task, then open +To see the demo: run `demo/spatial-showcase/Tasks/distribution/installJsDist` Gradle task, then open `build/distribuions/spatial-showcase-js-0.1.0-dev/index.html` file in your browser. -Other demos can be built similarly. +##### Example view: -##### muon-monitor +![](doc/resources/spatial-showcase.png) + +### Muon Monitor Visualization A full-stack application example, showing the [Muon Monitor](http://npm.mipt.ru/projects/physics.html#mounMonitor) experiment set-up. @@ -68,8 +129,19 @@ Includes server back-end generating events, as well as visualization front-end. To run full-stack app (both server and browser front-end), run `demo/muon-monitor/application/run` task. -##### gdml +##### Example view: -Visualization example for geometry defined as GDML file. Once you open Web application, -drag-and-drop GDML file to the window to see visualization. For example file, use -`demo\gdml\src\jsMain\resources\cubes.gdml`. +![](doc/resources/muon-monitor.png) + +### GDML Example + +Visualization example for geometry defined as GDML file. + +To build the app, run `demo/gdml/Tasks/distribution/installJsDist` task, then open +`build/distribuions/gdml-js-0.1.0-dev/index.html` file in your browser, and +drag-and-drop GDML file to the window to see visualization. For an example file, use +`demo/gdml/src/jsMain/resources/cubes.gdml`. + +##### Example view: + +![](doc/resources/gdml-demo.png) diff --git a/build.gradle.kts b/build.gradle.kts index 0378807d..c1218820 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -26,7 +26,7 @@ allprojects { } group = "hep.dataforge" - version = "0.1.0-dev" + version = "0.1.1-dev" } subprojects{ diff --git a/dataforge-vis-common/build.gradle.kts b/dataforge-vis-common/build.gradle.kts index 548c1be5..6459c337 100644 --- a/dataforge-vis-common/build.gradle.kts +++ b/dataforge-vis-common/build.gradle.kts @@ -37,7 +37,7 @@ kotlin { jsMain{ dependencies { api("hep.dataforge:dataforge-output-html:$dataforgeVersion") - api(npm("bootstrap","4.4.1")) + //api(npm("bootstrap","4.4.1")) implementation(npm("jsoneditor")) implementation(npm("file-saver")) } diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/AbstractVisualGroup.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/AbstractVisualGroup.kt index 889cb0dc..a0af25b7 100644 --- a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/AbstractVisualGroup.kt +++ b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/AbstractVisualGroup.kt @@ -1,7 +1,10 @@ package hep.dataforge.vis.common import hep.dataforge.meta.MetaItem -import hep.dataforge.names.* +import hep.dataforge.names.Name +import hep.dataforge.names.NameToken +import hep.dataforge.names.asName +import hep.dataforge.names.isEmpty import kotlinx.serialization.Transient @@ -76,7 +79,7 @@ abstract class AbstractVisualGroup : AbstractVisualObject(), MutableVisualGroup * 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. */ - override fun set(name: Name, child: VisualObject?) { + override fun set(name: Name, child: VisualObject?): Unit { when { name.isEmpty() -> { if (child != null) { @@ -100,13 +103,4 @@ abstract class AbstractVisualGroup : AbstractVisualObject(), MutableVisualGroup structureChangeListeners.forEach { it.callback(name, child) } } - operator fun set(key: String, child: VisualObject?): Unit { - if (key.isBlank()) { - if(child!= null) { - addStatic(child) - } - } else { - set(key.toName(), child) - } - } } \ No newline at end of file diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/AbstractVisualObject.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/AbstractVisualObject.kt index 7701eb1c..5487a304 100644 --- a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/AbstractVisualObject.kt +++ b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/AbstractVisualObject.kt @@ -73,6 +73,9 @@ abstract class AbstractVisualObject : VisualObject { styleCache = it } + /** + * All available properties in a layered form + */ override fun allProperties(): Laminate = Laminate(properties, mergedStyles) override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? { diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/StyleSheet.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/StyleSheet.kt index fce0b422..7b70a0dc 100644 --- a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/StyleSheet.kt +++ b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/StyleSheet.kt @@ -6,10 +6,11 @@ import hep.dataforge.io.serialization.MetaSerializer import hep.dataforge.meta.* import hep.dataforge.names.Name import hep.dataforge.names.asName -import kotlinx.serialization.Serializable -import kotlinx.serialization.Transient -import kotlinx.serialization.UseSerializers +import kotlinx.serialization.* +/** + * A container for styles + */ @Serializable class StyleSheet() { @Transient @@ -28,7 +29,7 @@ class StyleSheet() { } /** - * Define a style without notifying + * Define a style without notifying owner */ fun define(key: String, style: Meta?) { if (style == null) { @@ -48,6 +49,20 @@ class StyleSheet() { val newStyle = get(key)?.let { buildMeta(it, builder) } ?: buildMeta(builder) set(key, newStyle.seal()) } + + companion object: KSerializer{ + override val descriptor: SerialDescriptor + get() = TODO("Not yet implemented") + + override fun deserialize(decoder: Decoder): StyleSheet { + TODO("Not yet implemented") + } + + override fun serialize(encoder: Encoder, obj: StyleSheet) { + TODO("Not yet implemented") + } + + } } private fun VisualObject.styleChanged(key: String, oldStyle: Meta?, newStyle: Meta?) { diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualPlugin.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/Visual.kt similarity index 82% rename from dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualPlugin.kt rename to dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/Visual.kt index 39a1abc0..a469ac63 100644 --- a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualPlugin.kt +++ b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/Visual.kt @@ -13,7 +13,7 @@ interface VisualFactory { ): T } -class VisualPlugin(meta: Meta) : AbstractPlugin(meta) { +class Visual(meta: Meta) : AbstractPlugin(meta) { override val tag: PluginTag get() = Companion.tag /** @@ -27,11 +27,11 @@ class VisualPlugin(meta: Meta) : AbstractPlugin(meta) { return visualFactories[T::class]?.invoke(context, parent, meta) as T? } - companion object : PluginFactory { + companion object : PluginFactory { override val tag: PluginTag = PluginTag(name = "visual", group = PluginTag.DATAFORGE_GROUP) - override val type: KClass = VisualPlugin::class + override val type: KClass = Visual::class - override fun invoke(meta: Meta, context: Context): VisualPlugin = VisualPlugin(meta) + override fun invoke(meta: Meta, context: Context): Visual = Visual(meta) const val VISUAL_FACTORY_TYPE = "visual.factory" } 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 467616c3..c3f497bd 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 @@ -16,6 +16,10 @@ interface VisualGroup : Provider, Iterable, VisualObject { val styleSheet: StyleSheet? + /** + * A map of direct children for specific target + * (currently "visual" or "style") + */ override fun provideTop(target: String): Map = when (target) { VisualObject.TYPE -> children.flatMap { (key, value) -> @@ -49,7 +53,7 @@ interface VisualGroup : Provider, Iterable, VisualObject { */ fun attachChildren() { styleSheet?.owner = this - this.children.values.forEach { + children.values.forEach { it.parent = this (it as? VisualGroup)?.attachChildren() } @@ -84,6 +88,10 @@ interface MutableVisualGroup : VisualGroup { operator fun set(name: Name, child: VisualObject?) } -operator fun VisualGroup.get(str: String?) = get(str?.toName() ?: Name.EMPTY) +operator fun VisualGroup.get(str: String?): VisualObject? = get(str?.toName() ?: Name.EMPTY) + +operator fun MutableVisualGroup.set(key: String, child: VisualObject?) { + set(key.toName(), child) +} fun MutableVisualGroup.removeAll() = children.keys.map { it.asName() }.forEach { this[it] = null } \ No newline at end of file diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualObjectDelegate.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualObjectDelegate.kt index 1dba2c7f..ec16f977 100644 --- a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualObjectDelegate.kt +++ b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualObjectDelegate.kt @@ -2,9 +2,7 @@ package hep.dataforge.vis.common import hep.dataforge.meta.* import hep.dataforge.names.Name -import hep.dataforge.names.NameToken import hep.dataforge.names.asName -import hep.dataforge.names.toName import hep.dataforge.values.Value import kotlin.jvm.JvmName import kotlin.properties.ReadOnlyProperty @@ -48,7 +46,7 @@ class VisualObjectDelegateWrapper( override fun getValue(thisRef: Any?, property: KProperty<*>): T { val name = key ?: property.name.asName() - return read(obj.getProperty(name,inherited))?:default + return read(obj.getProperty(name, inherited)) ?: default } override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { @@ -58,75 +56,72 @@ class VisualObjectDelegateWrapper( } -fun VisualObject.value(default: Value? = null, key: String? = null, inherited: Boolean = false) = - VisualObjectDelegateWrapper(this, key?.toName(), default, inherited) { it.value } +fun VisualObject.value(default: Value? = null, name: Name? = null, inherited: Boolean = false) = + VisualObjectDelegateWrapper(this, name, default, inherited) { it.value } -fun VisualObject.string(default: String? = null, key: String? = null, inherited: Boolean = false) = - VisualObjectDelegateWrapper(this, key?.toName(), default, inherited) { it.string } +fun VisualObject.string(default: String? = null, name: Name? = null, inherited: Boolean = false) = + VisualObjectDelegateWrapper(this, name, default, inherited) { it.string } -fun VisualObject.boolean(default: Boolean? = null, key: String? = null, inherited: Boolean = false) = - VisualObjectDelegateWrapper(this, key?.toName(), default, inherited) { it.boolean } +fun VisualObject.boolean(default: Boolean? = null, name: Name? = null, inherited: Boolean = false) = + VisualObjectDelegateWrapper(this, name, default, inherited) { it.boolean } -fun VisualObject.number(default: Number? = null, key: String? = null, inherited: Boolean = false) = - VisualObjectDelegateWrapper(this, key?.toName(), default, inherited) { it.number } +fun VisualObject.number(default: Number? = null, name: Name? = null, inherited: Boolean = false) = + VisualObjectDelegateWrapper(this, name, default, inherited) { it.number } -fun VisualObject.double(default: Double? = null, key: String? = null, inherited: Boolean = false) = - VisualObjectDelegateWrapper(this, key?.toName(), default, inherited) { it.double } +fun VisualObject.double(default: Double? = null, name: Name? = null, inherited: Boolean = false) = + VisualObjectDelegateWrapper(this, name, default, inherited) { it.double } -fun VisualObject.int(default: Int? = null, key: String? = null, inherited: Boolean = false) = - VisualObjectDelegateWrapper(this, key?.toName(), default, inherited) { it.int } +fun VisualObject.int(default: Int? = null, name: Name? = null, inherited: Boolean = false) = + VisualObjectDelegateWrapper(this, name, default, inherited) { it.int } -fun VisualObject.node(key: String? = null, inherited: Boolean = true) = - VisualObjectDelegateWrapper(this, key?.toName(), null, inherited) { it.node } +fun VisualObject.node(name: Name? = null, inherited: Boolean = true) = + VisualObjectDelegateWrapper(this, name, null, inherited) { it.node } -fun VisualObject.item(key: String? = null, inherited: Boolean = true) = - VisualObjectDelegateWrapper(this, key?.toName(), null, inherited) { it } +fun VisualObject.item(name: Name? = null, inherited: Boolean = true) = + VisualObjectDelegateWrapper(this, name, 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) = - VisualObjectDelegateWrapper(this, key?.toName(), default, inherited) { it.string } +fun VisualObject.string(default: String, name: Name? = null, inherited: Boolean = false) = + VisualObjectDelegateWrapper(this, name, default, inherited) { it.string } @JvmName("safeBoolean") -fun VisualObject.boolean(default: Boolean, key: String? = null, inherited: Boolean = false) = - VisualObjectDelegateWrapper(this, key?.toName(), default, inherited) { it.boolean } +fun VisualObject.boolean(default: Boolean, name: Name? = null, inherited: Boolean = false) = + VisualObjectDelegateWrapper(this, name, default, inherited) { it.boolean } @JvmName("safeNumber") -fun VisualObject.number(default: Number, key: String? = null, inherited: Boolean = false) = - VisualObjectDelegateWrapper(this, key?.toName(), default, inherited) { it.number } +fun VisualObject.number(default: Number, name: Name? = null, inherited: Boolean = false) = + VisualObjectDelegateWrapper(this, name, default, inherited) { it.number } @JvmName("safeDouble") -fun VisualObject.double(default: Double, key: String? = null, inherited: Boolean = false) = - VisualObjectDelegateWrapper(this, key?.toName(), default, inherited) { it.double } +fun VisualObject.double(default: Double, name: Name? = null, inherited: Boolean = false) = + VisualObjectDelegateWrapper(this, name, default, inherited) { it.double } @JvmName("safeInt") -fun VisualObject.int(default: Int, key: String? = null, inherited: Boolean = false) = - VisualObjectDelegateWrapper(this, key?.toName(), default, inherited) { it.int } +fun VisualObject.int(default: Int, name: Name? = null, inherited: Boolean = false) = + VisualObjectDelegateWrapper(this, name, default, inherited) { it.int } -inline fun > VisualObject.enum(default: E, key: String? = null, inherited: Boolean = false) = - VisualObjectDelegateWrapper( - this, - key?.let { NameToken(it).asName() }, - default, - inherited - ) { item -> item.string?.let { enumValueOf(it) } } +inline fun > VisualObject.enum(default: E, name: Name? = null, inherited: Boolean = false) = + VisualObjectDelegateWrapper(this, name, default, inherited) { + item -> item.string?.let { enumValueOf(it) } + } //merge properties fun VisualObject.merge( - key: String? = null, + name: Name? = null, transformer: (Sequence>) -> T ): ReadOnlyProperty { return object : ReadOnlyProperty { override fun getValue(thisRef: Any?, property: KProperty<*>): T { - val name = key?.toName() ?: property.name.asName() + val actualName = name ?: property.name.asName() val sequence = sequence> { var thisObj: VisualObject? = this@merge while (thisObj != null) { - thisObj.config[name]?.let { yield(it) } + thisObj.config[actualName]?.let { yield(it) } thisObj = thisObj.parent } } diff --git a/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/js/editor/bootstrap.kt b/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/js/editor/bootstrap.kt index 2f1e4595..171aa86d 100644 --- a/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/js/editor/bootstrap.kt +++ b/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/js/editor/bootstrap.kt @@ -1,8 +1,7 @@ package hep.dataforge.vis.js.editor -import kotlinx.html.TagConsumer +import kotlinx.html.* import kotlinx.html.js.div -import kotlinx.html.js.h3 import org.w3c.dom.HTMLElement inline fun TagConsumer.card(title: String, crossinline block: TagConsumer.() -> Unit) { @@ -12,4 +11,51 @@ inline fun TagConsumer.card(title: String, crossinline block: TagCo block() } } +} + +fun TagConsumer.accordion(id: String, elements: Map Unit>) { + div("container-fluid") { + div("accordion") { + this.id = id + elements.entries.forEachIndexed { index, (title, builder) -> + val headerID = "${id}-${index}-heading" + val collapseID = "${id}-${index}-collapse" + div("card") { + div("card-header") { + this.id = headerID + h5("mb-0") { + button(classes = "btn btn-link collapsed", type = ButtonType.button) { + attributes["data-toggle"] = "collapse" + attributes["data-target"] = "#$collapseID" + attributes["aria-expanded"] = "false" + attributes["aria-controls"] = collapseID + +title + } + } + } + div("collapse") { + this.id = collapseID + attributes["aria-labelledby"] = headerID + attributes["data-parent"] = "#$id" + div("card-body", block = builder) + } + } + } + } + } +} + +class AccordionBuilder { + private val map = HashMap Unit>() + fun entry(title: String, block: DIV.() -> Unit) { + map[title] = block + } + + fun build(consumer: TagConsumer, id: String) { + consumer.accordion(id, map) + } +} + +fun TagConsumer.accordion(id: String, block: AccordionBuilder.() -> Unit) { + AccordionBuilder().apply(block).build(this, id) } \ No newline at end of file diff --git a/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/js/editor/jsTree.kt b/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/js/editor/jsTree.kt index 3cf4a985..dde67544 100644 --- a/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/js/editor/jsTree.kt +++ b/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/js/editor/jsTree.kt @@ -26,12 +26,11 @@ fun Element.displayObjectTree( } private fun TagConsumer.subTree( - fullName: Name, + name: Name, obj: VisualObject, clickCallback: (Name) -> Unit ) { -// val fullName = parentName + token - val token = fullName.last()?.toString()?:"World" + val token = name.last()?.toString()?:"World" //display as node if any child is visible if (obj is VisualGroup && obj.children.keys.any { !it.body.startsWith("@") }) { @@ -40,7 +39,7 @@ private fun TagConsumer.subTree( toggle = span("objTree-caret") label("objTree-label") { +token - onClickFunction = { clickCallback(fullName) } + onClickFunction = { clickCallback(name) } } } val subtree = ul("objTree-subtree") @@ -55,7 +54,7 @@ private fun TagConsumer.subTree( .forEach { (childToken, child) -> append { li().apply { - subTree(fullName + childToken, child, clickCallback) + subTree(name + childToken, child, clickCallback) } } } @@ -70,7 +69,7 @@ private fun TagConsumer.subTree( span("objTree-leaf") label("objTree-label") { +token - onClickFunction = { clickCallback(fullName) } + onClickFunction = { clickCallback(name) } } } } diff --git a/dataforge-vis-spatial-gdml/build.gradle.kts b/dataforge-vis-spatial-gdml/build.gradle.kts index 2d8a4200..c13e2e3f 100644 --- a/dataforge-vis-spatial-gdml/build.gradle.kts +++ b/dataforge-vis-spatial-gdml/build.gradle.kts @@ -7,7 +7,7 @@ kotlin { val commonMain by getting { dependencies { api(project(":dataforge-vis-spatial")) - api("scientifik:gdml:0.1.4") + api("scientifik:gdml:0.1.6") } } } 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 8fc395e1..1f72a899 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 @@ -5,6 +5,7 @@ import hep.dataforge.names.Name import hep.dataforge.names.asName import hep.dataforge.names.plus import hep.dataforge.vis.common.get +import hep.dataforge.vis.common.set import hep.dataforge.vis.spatial.* import hep.dataforge.vis.spatial.World.ONE import hep.dataforge.vis.spatial.World.ZERO @@ -66,6 +67,12 @@ private fun VisualGroup3D.addSolid( solid.deltaphi * aScale, name ) + is GDMLCone -> cone(solid.rmax1, solid.z, solid.rmax2, name = name) { + require(solid.rmin1 == 0.0) { "Empty cones are not supported" } + require(solid.rmin2 == 0.0) { "Empty cones are not supported" } + startAngle = solid.startphi.toFloat() + angle = solid.deltaphi.toFloat() + } is GDMLXtru -> extrude(name) { shape { solid.vertices.forEach { @@ -171,7 +178,7 @@ private fun VisualGroup3D.addPhysicalVolume( context.proto[fullName] = volume(context, volume) } - this[physVolume.name ?: ""] = Proxy(fullName).apply { + this[physVolume.name ?: ""] = Proxy(this, fullName).apply { withPosition( context.lUnit, physVolume.resolvePosition(context.root), diff --git a/dataforge-vis-spatial-gdml/src/jvmMain/kotlin/hep/dataforge/vis/spatial/gdml/generateSchema.kt b/dataforge-vis-spatial-gdml/src/jvmMain/kotlin/hep/dataforge/vis/spatial/gdml/generateSchema.kt index 628a8651..53768de4 100644 --- a/dataforge-vis-spatial-gdml/src/jvmMain/kotlin/hep/dataforge/vis/spatial/gdml/generateSchema.kt +++ b/dataforge-vis-spatial-gdml/src/jvmMain/kotlin/hep/dataforge/vis/spatial/gdml/generateSchema.kt @@ -135,7 +135,7 @@ private fun jsonSchema(descriptor: SerialDescriptor, context: SerialModule): Jso } fun main() { - val context = Visual3DPlugin.serialModule + val context = Visual3D.serialModule val definitions = json { "children" to json { "anyOf" to jsonArray { diff --git a/dataforge-vis-spatial-gdml/src/jvmMain/kotlin/hep/dataforge/vis/spatial/gdml/visualGDMLJvm.kt b/dataforge-vis-spatial-gdml/src/jvmMain/kotlin/hep/dataforge/vis/spatial/gdml/visualGDMLJvm.kt index ae1904f5..1127cead 100644 --- a/dataforge-vis-spatial-gdml/src/jvmMain/kotlin/hep/dataforge/vis/spatial/gdml/visualGDMLJvm.kt +++ b/dataforge-vis-spatial-gdml/src/jvmMain/kotlin/hep/dataforge/vis/spatial/gdml/visualGDMLJvm.kt @@ -8,7 +8,7 @@ import java.nio.file.Path fun GDML.Companion.readFile(file: Path): GDML { val xmlReader = StAXReader(Files.newInputStream(file), "UTF-8") - return GDML.format.parse(GDML.serializer(), xmlReader) + return format.parse(GDML.serializer(), xmlReader) } fun VisualGroup3D.gdml(file: Path, key: String = "", transformer: GDMLTransformer.() -> Unit = {}) { diff --git a/dataforge-vis-spatial/build.gradle.kts b/dataforge-vis-spatial/build.gradle.kts index 2fe2ac14..f587d39d 100644 --- a/dataforge-vis-spatial/build.gradle.kts +++ b/dataforge-vis-spatial/build.gradle.kts @@ -20,7 +20,7 @@ kotlin { } jvmMain { dependencies { - implementation("org.fxyz3d:fxyz3d:0.5.2") { + api("org.fxyz3d:fxyz3d:0.5.2") { exclude(module = "slf4j-simple") } api("org.jetbrains.kotlinx:kotlinx-coroutines-javafx:${Scientifik.coroutinesVersion}") 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 fd6358a2..c2010a0f 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 @@ -10,13 +10,15 @@ import hep.dataforge.meta.get import hep.dataforge.vis.common.AbstractVisualObject import hep.dataforge.vis.common.VisualFactory import hep.dataforge.vis.common.VisualObject +import hep.dataforge.vis.common.set +import hep.dataforge.vis.spatial.Box.Companion.TYPE_NAME import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.UseSerializers import kotlin.reflect.KClass @Serializable -@SerialName("3d.box") +@SerialName(TYPE_NAME) class Box( val xSize: Float, val ySize: Float, @@ -52,7 +54,8 @@ class Box( } companion object : VisualFactory { - const val TYPE = "geometry.3d.box" + + const val TYPE_NAME = "3d.box" override val type: KClass get() = Box::class 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 ed6f087b..a7b3c774 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 @@ -6,6 +6,8 @@ import hep.dataforge.io.serialization.ConfigSerializer import hep.dataforge.meta.Config import hep.dataforge.meta.update import hep.dataforge.vis.common.AbstractVisualObject +import hep.dataforge.vis.common.set +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.UseSerializers @@ -16,6 +18,7 @@ enum class CompositeType { } @Serializable +@SerialName("3d.composite") class Composite( val compositeType: CompositeType, val first: VisualObject3D, diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/ConeSegment.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/ConeSegment.kt index 9aa090ef..f373a8de 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/ConeSegment.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/ConeSegment.kt @@ -5,6 +5,8 @@ package hep.dataforge.vis.spatial import hep.dataforge.io.serialization.ConfigSerializer import hep.dataforge.meta.Config import hep.dataforge.vis.common.AbstractVisualObject +import hep.dataforge.vis.common.set +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.UseSerializers import kotlin.math.cos @@ -14,6 +16,7 @@ import kotlin.math.sin * A cylinder or cut cone segment */ @Serializable +@SerialName("3d.cone") class ConeSegment( var radius: Float, var height: Float, @@ -82,4 +85,17 @@ inline fun VisualGroup3D.cylinder( r.toFloat(), height.toFloat(), r.toFloat() +).apply(block).also { set(name, it) } + + +inline fun VisualGroup3D.cone( + bottomRadius: Number, + height: Number, + upperRadius: Number = 0.0, + name: String = "", + block: ConeSegment.() -> Unit = {} +): ConeSegment = ConeSegment( + bottomRadius.toFloat(), + height.toFloat(), + upperRadius = upperRadius.toFloat() ).apply(block).also { set(name, it) } \ 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 1ca46886..e4168ed6 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 @@ -5,10 +5,13 @@ package hep.dataforge.vis.spatial import hep.dataforge.io.serialization.ConfigSerializer import hep.dataforge.meta.Config import hep.dataforge.vis.common.AbstractVisualObject +import hep.dataforge.vis.common.set +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.UseSerializers @Serializable +@SerialName("3d.convex") class Convex(val points: List) : AbstractVisualObject(), VisualObject3D { @Serializable(ConfigSerializer::class) 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 18dac3d0..ae81eace 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 @@ -4,6 +4,8 @@ package hep.dataforge.vis.spatial import hep.dataforge.io.serialization.ConfigSerializer import hep.dataforge.meta.Config import hep.dataforge.vis.common.AbstractVisualObject +import hep.dataforge.vis.common.set +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.UseSerializers import kotlin.math.PI @@ -37,6 +39,7 @@ fun Shape2DBuilder.polygon(vertices: Int, radius: Number) { data class Layer(var x: Float, var y: Float, var z: Float, var scale: Float) @Serializable +@SerialName("3d.extrude") class Extruded( var shape: List = ArrayList(), var layers: MutableList = ArrayList() diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/GeometryBuilder.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/GeometryBuilder.kt index 07ea1612..efc623d9 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/GeometryBuilder.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/GeometryBuilder.kt @@ -31,6 +31,9 @@ fun GeometryBuilder<*>.face4( face(vertex1, vertex3, vertex4, normal, meta) } +/** + * [Shape] is a [VisualObject3D] that can represent its own geometry as a set of polygons. + */ interface Shape : VisualObject3D { fun toGeometry(geometryBuilder: GeometryBuilder) } diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Label3D.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Label3D.kt index 01ff005e..906b03de 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Label3D.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Label3D.kt @@ -5,10 +5,13 @@ package hep.dataforge.vis.spatial import hep.dataforge.io.serialization.ConfigSerializer import hep.dataforge.meta.Config import hep.dataforge.vis.common.AbstractVisualObject +import hep.dataforge.vis.common.set +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.UseSerializers @Serializable +@SerialName("3d.label") class Label3D(var text: String, var fontSize: Double, var fontFamily: String) : AbstractVisualObject(), VisualObject3D { @Serializable(ConfigSerializer::class) diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Material3D.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Material3D.kt index 342016cb..f489c62a 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Material3D.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Material3D.kt @@ -7,16 +7,29 @@ import hep.dataforge.names.plus import hep.dataforge.values.ValueType import hep.dataforge.vis.common.Colors import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_COLOR_KEY +import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_KEY import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_OPACITY_KEY class Material3D(override val config: Config) : Specific { + /** + * Primary web-color for the material + */ var color by string(key = COLOR_KEY) - var specularColor by string() + /** + * Specular color for phong material + */ + var specularColor by string(key = SPECULAR_COLOR_KEY) + /** + * Opacity + */ var opacity by float(1f, key = OPACITY_KEY) + /** + * Replace material by wire frame + */ var wireframe by boolean(false, WIREFRAME_KEY) companion object : Specification { @@ -25,7 +38,7 @@ class Material3D(override val config: Config) : Specific { val MATERIAL_KEY = "material".asName() internal val COLOR_KEY = "color".asName() val MATERIAL_COLOR_KEY = MATERIAL_KEY + COLOR_KEY - val SPECULAR_COLOR = "specularColor".asName() + val SPECULAR_COLOR_KEY = "specularColor".asName() internal val OPACITY_KEY = "opacity".asName() val MATERIAL_OPACITY_KEY = MATERIAL_KEY + OPACITY_KEY internal val WIREFRAME_KEY = "wireframe".asName() @@ -54,10 +67,16 @@ class Material3D(override val config: Config) : Specific { } } -fun VisualObject3D.color(rgb: String) { - setProperty(MATERIAL_COLOR_KEY, rgb) +/** + * Set color as web-color + */ +fun VisualObject3D.color(webColor: String) { + setProperty(MATERIAL_COLOR_KEY, webColor) } +/** + * Set color as integer + */ fun VisualObject3D.color(rgb: Int) { setProperty(MATERIAL_COLOR_KEY, rgb) } @@ -76,16 +95,15 @@ var VisualObject3D.color: String? setProperty(MATERIAL_COLOR_KEY, value) } -//var VisualObject3D.material: Material3D? -// get() = getProperty(MATERIAL_KEY).node?.let { Material3D.wrap(it) } -// set(value) = setProperty(MATERIAL_KEY, value?.config) +val VisualObject3D.material: Material3D? + get() = getProperty(MATERIAL_KEY).node?.let { Material3D.wrap(it) } fun VisualObject3D.material(builder: Material3D.() -> Unit) { - val node = config[Material3D.MATERIAL_KEY].node + val node = config[MATERIAL_KEY].node if (node != null) { Material3D.update(node, builder) } else { - config[Material3D.MATERIAL_KEY] = Material3D(builder) + config[MATERIAL_KEY] = Material3D(builder) } } diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/PolyLine.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/PolyLine.kt index 75d00ab4..24f5a3b9 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/PolyLine.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/PolyLine.kt @@ -4,12 +4,17 @@ package hep.dataforge.vis.spatial import hep.dataforge.io.serialization.ConfigSerializer import hep.dataforge.meta.Config +import hep.dataforge.meta.number +import hep.dataforge.names.asName +import hep.dataforge.names.plus import hep.dataforge.vis.common.AbstractVisualObject -import hep.dataforge.vis.common.number +import hep.dataforge.vis.common.set +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.UseSerializers @Serializable +@SerialName("3d.line") class PolyLine(var points: List) : AbstractVisualObject(), VisualObject3D { @Serializable(ConfigSerializer::class) override var properties: Config? = null @@ -19,7 +24,11 @@ class PolyLine(var points: List) : AbstractVisualObject(), VisualObject override var scale: Point3D? = null //var lineType by string() - var thickness by number(1.0, key = "material.thickness") + var thickness by number(1.0, key = Material3D.MATERIAL_KEY + THICKNESS_KEY) + + companion object { + val THICKNESS_KEY = "thickness".asName() + } } diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Proxy.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Proxy.kt index 62d7b5c0..9b72e6d5 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Proxy.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Proxy.kt @@ -26,7 +26,11 @@ import kotlin.collections.set */ @Serializable @SerialName("3d.proxy") -class Proxy(val templateName: Name) : AbstractVisualObject(), VisualGroup, VisualObject3D { +class Proxy private constructor(val templateName: Name) : AbstractVisualObject(), VisualGroup, VisualObject3D { + + constructor(parent: VisualGroup3D, templateName: Name) : this(templateName) { + this.parent = parent + } override var position: Point3D? = null override var rotation: Point3D? = null @@ -59,7 +63,7 @@ class Proxy(val templateName: Name) : AbstractVisualObject(), VisualGroup, Visua } override val children: Map - get() = (prototype as? MutableVisualGroup)?.children + get() = (prototype as? VisualGroup)?.children ?.filter { !it.key.toString().startsWith("@") } ?.mapValues { ProxyChild(it.key.asName()) @@ -79,9 +83,12 @@ class Proxy(val templateName: Name) : AbstractVisualObject(), VisualGroup, Visua override fun allProperties(): Laminate = Laminate(properties, mergedStyles, prototype.allProperties()) + override fun attachChildren() { + //do nothing + } + //override fun findAllStyles(): Laminate = Laminate((styles + prototype.styles).mapNotNull { findStyle(it) }) - @Serializable inner class ProxyChild(val name: Name) : AbstractVisualObject(), VisualGroup { val prototype: VisualObject get() = prototypeFor(name) @@ -125,6 +132,9 @@ class Proxy(val templateName: Name) : AbstractVisualObject(), VisualGroup, Visua } } + override fun attachChildren() { + //do nothing + } override fun allProperties(): Laminate = Laminate(properties, mergedStyles, prototype.allProperties()) @@ -149,7 +159,7 @@ inline fun VisualGroup3D.ref( templateName: Name, name: String = "", block: Proxy.() -> Unit = {} -) = Proxy(templateName).apply(block).also { set(name, it) } +) = Proxy(this, templateName).apply(block).also { set(name, it) } /** * Add new proxy wrapping given object and automatically adding it to the prototypes @@ -162,7 +172,9 @@ fun VisualGroup3D.proxy( ): Proxy { val existing = getPrototype(templateName) if (existing == null) { - setPrototype(templateName, obj) + prototypes { + this[templateName] = obj + } } else if (existing != obj) { error("Can't add different prototype on top of existing one") } 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 58e2c8b6..b29af3c8 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 @@ -5,6 +5,8 @@ package hep.dataforge.vis.spatial import hep.dataforge.io.serialization.ConfigSerializer import hep.dataforge.meta.Config import hep.dataforge.vis.common.AbstractVisualObject +import hep.dataforge.vis.common.set +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.UseSerializers import kotlin.math.PI @@ -12,6 +14,7 @@ import kotlin.math.cos import kotlin.math.sin @Serializable +@SerialName("3d.sphere") class Sphere( var radius: Float, var phiStart: Float = 0f, diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Tube.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Tube.kt index 248fbfce..d97ccce6 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Tube.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Tube.kt @@ -4,6 +4,8 @@ package hep.dataforge.vis.spatial import hep.dataforge.io.serialization.ConfigSerializer import hep.dataforge.meta.Config import hep.dataforge.vis.common.AbstractVisualObject +import hep.dataforge.vis.common.set +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.UseSerializers import kotlin.math.PI @@ -14,6 +16,7 @@ import kotlin.math.sin * Straight tube segment */ @Serializable +@SerialName("3d.tube") class Tube( var radius: Float, var height: Float, diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Visual3DPlugin.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Visual3D.kt similarity index 83% rename from dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Visual3DPlugin.kt rename to dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Visual3D.kt index 3f4a194d..cb362558 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Visual3DPlugin.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Visual3D.kt @@ -9,29 +9,32 @@ import hep.dataforge.io.serialization.MetaSerializer import hep.dataforge.io.serialization.NameSerializer import hep.dataforge.meta.* import hep.dataforge.names.Name +import hep.dataforge.names.toName +import hep.dataforge.vis.common.Visual import hep.dataforge.vis.common.VisualObject -import hep.dataforge.vis.common.VisualPlugin import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonConfiguration import kotlinx.serialization.modules.SerializersModule import kotlinx.serialization.modules.contextual import kotlin.reflect.KClass -class Visual3DPlugin(meta: Meta) : AbstractPlugin(meta) { +class Visual3D(meta: Meta) : AbstractPlugin(meta) { + + val visual by require(Visual) + override val tag: PluginTag get() = Companion.tag - override fun provideTop(target: String): Map { - return if (target == VisualPlugin.VISUAL_FACTORY_TYPE) { - mapOf() - } else { - emptyMap() - } + override fun provideTop(target: String): Map = if (target == Visual.VISUAL_FACTORY_TYPE) { + mapOf(Box.TYPE_NAME.toName() to Box) + } else { + super.provideTop(target) } - companion object : PluginFactory { + + companion object : PluginFactory { override val tag: PluginTag = PluginTag(name = "visual.spatial", group = PluginTag.DATAFORGE_GROUP) - override val type: KClass = Visual3DPlugin::class - override fun invoke(meta: Meta, context: Context): Visual3DPlugin = Visual3DPlugin(meta) + override val type: KClass = Visual3D::class + override fun invoke(meta: Meta, context: Context): Visual3D = Visual3D(meta) val serialModule = SerializersModule { contextual(Point3DSerializer) diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/VisualGroup3D.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/VisualGroup3D.kt index 44717f4d..402290f8 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/VisualGroup3D.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/VisualGroup3D.kt @@ -19,6 +19,7 @@ import hep.dataforge.names.isEmpty import hep.dataforge.vis.common.AbstractVisualGroup import hep.dataforge.vis.common.StyleSheet import hep.dataforge.vis.common.VisualObject +import hep.dataforge.vis.common.set import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.UseSerializers @@ -30,19 +31,19 @@ import kotlin.collections.set @Serializable @SerialName("group.3d") class VisualGroup3D : AbstractVisualGroup(), VisualObject3D { + + override var styleSheet: StyleSheet? = null + private set + /** * A container for templates visible inside this group */ - @SerialName(PROTOTYPES_KEY) var prototypes: VisualGroup3D? = null set(value) { value?.parent = this field = value } - override var styleSheet: StyleSheet? = null - private set - //FIXME to be lifted to AbstractVisualGroup after https://github.com/Kotlin/kotlinx.serialization/issues/378 is fixed override var properties: Config? = null @@ -54,28 +55,34 @@ class VisualGroup3D : AbstractVisualGroup(), VisualObject3D { private val _children = HashMap() override val children: Map get() = _children - init { - //Do after deserialization - attachChildren() +// init { +// //Do after deserialization +// attachChildren() +// } + + override fun attachChildren() { + prototypes?.parent = this + prototypes?.attachChildren() + super.attachChildren() } /** * Update or create stylesheet */ fun styleSheet(block: StyleSheet.() -> Unit) { - val res = this.styleSheet ?: StyleSheet(this).also { this.styleSheet = it } + val res = styleSheet ?: StyleSheet(this).also { styleSheet = it } res.block() } override fun removeChild(token: NameToken) { - _children.remove(token) + _children.remove(token)?.run { parent = null } childrenChanged(token.asName(), null) } override fun setChild(token: NameToken, child: VisualObject) { if (child.parent == null) { child.parent = this - } else { + } else if (child.parent !== this) { error("Can't reassign existing parent for $child") } _children[token] = child @@ -102,42 +109,32 @@ class VisualGroup3D : AbstractVisualGroup(), VisualObject3D { } } - override fun attachChildren() { - super.attachChildren() - prototypes?.run { - parent = this - attachChildren() - } - } - companion object { - const val PROTOTYPES_KEY = "templates" +// val PROTOTYPES_KEY = NameToken("@prototypes") + + fun fromJson(json: String): VisualGroup3D = + Visual3D.json.parse(serializer(), json).also { it.attachChildren() } } } /** * Ger a prototype redirecting the request to the parent if prototype is not found */ -fun VisualGroup3D.getPrototype(name: Name): VisualObject3D? = +tailrec fun VisualGroup3D.getPrototype(name: Name): VisualObject3D? = prototypes?.get(name) as? VisualObject3D ?: (parent as? VisualGroup3D)?.getPrototype(name) /** - * Defined a prototype inside current group + * Create or edit prototype node as a group */ -fun VisualGroup3D.setPrototype(name: Name, obj: VisualObject3D) { - (prototypes ?: VisualGroup3D().also { this.prototypes = it })[name] = obj +inline fun VisualGroup3D.prototypes(builder: VisualGroup3D.() -> Unit): Unit { + (prototypes ?: VisualGroup3D().also { prototypes = it }).run(builder) } /** * Define a group with given [key], attach it to this parent and return it. */ fun VisualGroup3D.group(key: String = "", action: VisualGroup3D.() -> Unit = {}): VisualGroup3D = - VisualGroup3D().apply(action).also { set(key, it) } - -/** - * Create or edit prototype node as a group - */ -inline fun VisualGroup3D.prototypes(builder: VisualGroup3D.() -> Unit): Unit { - (prototypes ?: VisualGroup3D().also { this.prototypes = it }).run(builder) -} + VisualGroup3D().apply(action).also { + set(key, it) + } 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 index db95754a..8e94ed65 100644 --- 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 @@ -22,22 +22,10 @@ interface VisualObject3D : VisualObject { var rotation: Point3D? var scale: Point3D? - fun MetaBuilder.updatePosition() { - xPos to position?.x - yPos to position?.y - zPos to position?.z - xRotation to rotation?.x - yRotation to rotation?.y - zRotation to rotation?.z - xScale to scale?.x - yScale to scale?.y - zScale to scale?.z - } - companion object { val VISIBLE_KEY = "visible".asName() - val SELECTED_KEY = "selected".asName() +// val SELECTED_KEY = "selected".asName() val DETAIL_KEY = "detail".asName() val LAYER_KEY = "layer".asName() val IGNORE_KEY = "ignore".asName() diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/serilization.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/serialization.kt similarity index 96% rename from dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/serilization.kt rename to dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/serialization.kt index b4f87d39..37115129 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/serilization.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/serialization.kt @@ -46,8 +46,8 @@ object Point3DSerializer : KSerializer { when (val i = decodeElementIndex(descriptor)) { CompositeDecoder.READ_DONE -> break@loop 0 -> x = decodeNullableSerializableElement(descriptor, 0, DoubleSerializer.nullable) ?: 0.0 - 1 -> y = decodeNullableSerializableElement(descriptor, 0, DoubleSerializer.nullable) ?: 0.0 - 2 -> z = decodeNullableSerializableElement(descriptor, 0, DoubleSerializer.nullable) ?: 0.0 + 1 -> y = decodeNullableSerializableElement(descriptor, 1, DoubleSerializer.nullable) ?: 0.0 + 2 -> z = decodeNullableSerializableElement(descriptor, 2, DoubleSerializer.nullable) ?: 0.0 else -> throw SerializationException("Unknown index $i") } } @@ -79,7 +79,7 @@ object Point2DSerializer : KSerializer { when (val i = decodeElementIndex(descriptor)) { CompositeDecoder.READ_DONE -> break@loop 0 -> x = decodeNullableSerializableElement(descriptor, 0, DoubleSerializer.nullable) ?: 0.0 - 1 -> y = decodeNullableSerializableElement(descriptor, 0, DoubleSerializer.nullable) ?: 0.0 + 1 -> y = decodeNullableSerializableElement(descriptor, 1, DoubleSerializer.nullable) ?: 0.0 else -> throw SerializationException("Unknown index $i") } } diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/transform/RemoveSingleChild.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/transform/RemoveSingleChild.kt index 70db5c75..636b9cc1 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/transform/RemoveSingleChild.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/transform/RemoveSingleChild.kt @@ -51,7 +51,9 @@ object RemoveSingleChild : VisualTreeTransform() { } replaceChildren() - prototypes?.replaceChildren() + prototypes { + replaceChildren() + } } override fun VisualGroup3D.clone(): VisualGroup3D { diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/transform/UnRef.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/transform/UnRef.kt index 12681b24..c96be89c 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/transform/UnRef.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/transform/UnRef.kt @@ -6,6 +6,7 @@ import hep.dataforge.vis.common.MutableVisualGroup import hep.dataforge.vis.common.VisualGroup import hep.dataforge.vis.spatial.Proxy import hep.dataforge.vis.spatial.VisualGroup3D +import hep.dataforge.vis.spatial.prototypes object UnRef : VisualTreeTransform() { private fun VisualGroup.countRefs(): Map { 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 3358fe84..0c2b4143 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 @@ -25,7 +25,7 @@ class ConvexTest { val convex = group.first() as Convex - val json = Visual3DPlugin.json.toJson(Convex.serializer(), convex) + val json = Visual3D.json.toJson(Convex.serializer(), convex) val meta = json.toMeta() val points = meta.getIndexed("points").values.map { (it as MetaItem.NodeItem<*>).node.point3D()} diff --git a/dataforge-vis-spatial/src/commonTest/kotlin/hep/dataforge/vis/spatial/SerializationTest.kt b/dataforge-vis-spatial/src/commonTest/kotlin/hep/dataforge/vis/spatial/SerializationTest.kt index 128b507b..ea242679 100644 --- a/dataforge-vis-spatial/src/commonTest/kotlin/hep/dataforge/vis/spatial/SerializationTest.kt +++ b/dataforge-vis-spatial/src/commonTest/kotlin/hep/dataforge/vis/spatial/SerializationTest.kt @@ -1,6 +1,6 @@ package hep.dataforge.vis.spatial -import hep.dataforge.vis.spatial.Visual3DPlugin.Companion.json +import hep.dataforge.vis.spatial.Visual3D.Companion.json import kotlinx.serialization.ImplicitReflectionSerializer import kotlin.test.Test import kotlin.test.assertEquals diff --git a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeFactory.kt b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeFactory.kt index 65dc71ef..6828cd7b 100644 --- a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeFactory.kt +++ b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeFactory.kt @@ -31,12 +31,14 @@ interface ThreeFactory { /** * Update position, rotation and visibility */ -fun Object3D.updatePosition(obj: VisualObject3D) { +fun Object3D.updatePosition(obj: VisualObject) { visible = obj.visible ?: true - position.set(obj.x, obj.y, obj.z) - setRotationFromEuler(obj.euler) - scale.set(obj.scaleX, obj.scaleY, obj.scaleZ) - updateMatrix() + if(obj is VisualObject3D) { + position.set(obj.x, obj.y, obj.z) + setRotationFromEuler(obj.euler) + scale.set(obj.scaleX, obj.scaleY, obj.scaleZ) + updateMatrix() + } } ///** @@ -54,7 +56,7 @@ fun Object3D.updatePosition(obj: VisualObject3D) { /** * Update non-position non-geometry property */ -fun Object3D.updateProperty(source: VisualObject3D, propertyName: Name) { +fun Object3D.updateProperty(source: VisualObject, propertyName: Name) { if (this is Mesh && propertyName.startsWith(MATERIAL_KEY)) { this.material = getMaterial(source) } else if ( diff --git a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeLabelFactory.kt b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeLabelFactory.kt index c3e1024a..54e0ae03 100644 --- a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeLabelFactory.kt +++ b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeLabelFactory.kt @@ -25,7 +25,7 @@ object ThreeLabelFactory : ThreeFactory { } ) return Mesh(textGeo, getMaterial(obj)).apply { updatePosition(obj) - obj.onPropertyChange(this@ThreeLabelFactory){name: Name, before: MetaItem<*>?, after: MetaItem<*>? -> + obj.onPropertyChange(this@ThreeLabelFactory){ _: Name, _: MetaItem<*>?, _: MetaItem<*>? -> //TODO } } diff --git a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeLineFactory.kt b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeLineFactory.kt index d2e8d52e..945bfc44 100644 --- a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeLineFactory.kt +++ b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeLineFactory.kt @@ -18,11 +18,10 @@ object ThreeLineFactory : ThreeFactory { vertices = obj.points.toTypedArray() } - val material = - ThreeMaterials.getLineMaterial(obj.getProperty(MeshThreeFactory.EDGES_MATERIAL_KEY).node) + val material = ThreeMaterials.getLineMaterial(obj.getProperty(MeshThreeFactory.EDGES_MATERIAL_KEY).node) material.linewidth = obj.thickness.toDouble() - material.color = obj.color?.let { Color(it) }?: DEFAULT_LINE_COLOR + material.color = obj.color?.let { Color(it) } ?: DEFAULT_LINE_COLOR return LineSegments(geometry, material).apply { updatePosition(obj) diff --git a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeMaterials.kt b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeMaterials.kt index ff3ddcf9..786c0efd 100644 --- a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeMaterials.kt +++ b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeMaterials.kt @@ -3,8 +3,8 @@ package hep.dataforge.vis.spatial.three import hep.dataforge.meta.* import hep.dataforge.values.ValueType import hep.dataforge.vis.common.Colors +import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.spatial.Material3D -import hep.dataforge.vis.spatial.VisualObject3D import info.laht.threekt.materials.LineBasicMaterial import info.laht.threekt.materials.Material import info.laht.threekt.materials.MeshBasicMaterial @@ -37,12 +37,12 @@ object ThreeMaterials { } } - fun getMaterial(visualObject3D: VisualObject3D): Material { + fun getMaterial(visualObject3D: VisualObject): Material { val meta = visualObject3D.getProperty(Material3D.MATERIAL_KEY).node ?: return ThreeMaterials.DEFAULT - return if (meta[Material3D.SPECULAR_COLOR] != null) { + return if (meta[Material3D.SPECULAR_COLOR_KEY] != null) { MeshPhongMaterial().apply { color = meta[Material3D.COLOR_KEY]?.getColor() ?: DEFAULT_COLOR - specular = meta[Material3D.SPECULAR_COLOR]!!.getColor() + specular = meta[Material3D.SPECULAR_COLOR_KEY]!!.getColor() opacity = meta[Material3D.OPACITY_KEY]?.double ?: 1.0 transparent = opacity < 1.0 wireframe = meta[Material3D.WIREFRAME_KEY].boolean ?: false diff --git a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreePlugin.kt b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreePlugin.kt index 11117b27..e5c99efe 100644 --- a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreePlugin.kt +++ b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreePlugin.kt @@ -74,8 +74,8 @@ class ThreePlugin : AbstractPlugin() { return@onChildrenChange } - val parentName = name.cutLast() - val childName = name.last()!! +// val parentName = name.cutLast() +// val childName = name.last()!! //removing old object findChild(name)?.let { oldChild -> diff --git a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeProxyFactory.kt b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeProxyFactory.kt index 84fcb86e..05bdd9e1 100644 --- a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeProxyFactory.kt +++ b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeProxyFactory.kt @@ -25,7 +25,7 @@ class ThreeProxyFactory(val three: ThreePlugin) : ThreeFactory { if (name.first()?.body == PROXY_CHILD_PROPERTY_PREFIX) { val childName = name.first()?.index?.toName() ?: error("Wrong syntax for proxy child property: '$name'") val propertyName = name.cutFirst() - val proxyChild = obj[childName] as? VisualObject3D ?: error("Proxy child with name '$childName' not found or not a 3D object") + val proxyChild = obj[childName] ?: error("Proxy child with name '$childName' not found") val child = object3D.findChild(childName)?: error("Object child with name '$childName' not found") child.updateProperty(proxyChild, propertyName) } else { diff --git a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/outputConfig.kt b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/outputConfig.kt index 1f3c0a6d..70afb07d 100644 --- a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/outputConfig.kt +++ b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/outputConfig.kt @@ -1,8 +1,8 @@ package hep.dataforge.vis.spatial.three import hep.dataforge.js.requireJS -import hep.dataforge.vis.js.editor.card -import hep.dataforge.vis.spatial.Visual3DPlugin +import hep.dataforge.vis.js.editor.accordion +import hep.dataforge.vis.spatial.Visual3D import hep.dataforge.vis.spatial.VisualGroup3D import kotlinx.html.InputType import kotlinx.html.TagConsumer @@ -28,7 +28,64 @@ private fun saveData(event: Event, fileName: String, mimeType: String = "text/pl fun Element.displayCanvasControls(canvas: ThreeCanvas, block: TagConsumer.() -> Unit = {}) { clear() append { - card("Settings") { + accordion("controls") { + entry("Settings") { + div("row") { + div("col-2") { + label("checkbox-inline") { + input(type = InputType.checkBox).apply { + checked = canvas.axes.visible + onChangeFunction = { + canvas.axes.visible = checked + } + } + +"Axes" + } + } + div("col-1") { + button { + +"Export" + onClickFunction = { + val json = (canvas.content as? VisualGroup3D)?.let { group -> + Visual3D.json.stringify( + VisualGroup3D.serializer(), + group + ) + } + if (json != null) { + saveData(it, "object.json", "text/json") { + json + } + } + } + } + } + } + } + entry("Layers") { + div("row") { + (0..11).forEach { layer -> + div("col-1") { + label { +layer.toString() } + input(type = InputType.checkBox).apply { + if (layer == 0) { + checked = true + } + onChangeFunction = { + if (checked) { + canvas.camera.layers.enable(layer) + } else { + canvas.camera.layers.disable(layer) + } + } + } + } + } + } + } + } + +/* card("Settings") { div("row") { div("col-2") { label("checkbox-inline") { @@ -46,7 +103,7 @@ fun Element.displayCanvasControls(canvas: ThreeCanvas, block: TagConsumer - Visual3DPlugin.json.stringify( + Visual3D.json.stringify( VisualGroup3D.serializer(), group ) @@ -81,7 +138,7 @@ fun Element.displayCanvasControls(canvas: ThreeCanvas, block: TagConsumer?.material(): Material { is MetaItem.NodeItem -> PhongMaterial().apply { val opacity = node[Material3D.OPACITY_KEY].double ?: 1.0 diffuseColor = node[Material3D.COLOR_KEY]?.color(opacity) ?: Color.DARKGREY - specularColor = node[Material3D.SPECULAR_COLOR]?.color(opacity) ?: Color.WHITE + specularColor = node[Material3D.SPECULAR_COLOR_KEY]?.color(opacity) ?: Color.WHITE } } } diff --git a/dataforge-vis-spatial/src/jvmMain/kotlin/hep/dataforge/vis/spatial/fx/FXProxyFactory.kt b/dataforge-vis-spatial/src/jvmMain/kotlin/hep/dataforge/vis/spatial/fx/FXProxyFactory.kt index 6cdb6610..6052afcd 100644 --- a/dataforge-vis-spatial/src/jvmMain/kotlin/hep/dataforge/vis/spatial/fx/FXProxyFactory.kt +++ b/dataforge-vis-spatial/src/jvmMain/kotlin/hep/dataforge/vis/spatial/fx/FXProxyFactory.kt @@ -13,8 +13,8 @@ class FXProxyFactory(val plugin: FX3DPlugin) : FX3DFactory { override val type: KClass get() = Proxy::class override fun invoke(obj: Proxy, binding: VisualObjectFXBinding): Node { - val template = obj.prototype - val node = plugin.buildNode(template) + val prototype = obj.prototype + val node = plugin.buildNode(prototype) obj.onPropertyChange(this) { name, _, _ -> if (name.first()?.body == Proxy.PROXY_CHILD_PROPERTY_PREFIX) { diff --git a/dataforge-vis-spatial/src/jvmMain/kotlin/hep/dataforge/vis/spatial/geometryJVM.kt b/dataforge-vis-spatial/src/jvmMain/kotlin/hep/dataforge/vis/spatial/geometryJVM.kt index 396d43b2..1d9c1a04 100644 --- a/dataforge-vis-spatial/src/jvmMain/kotlin/hep/dataforge/vis/spatial/geometryJVM.kt +++ b/dataforge-vis-spatial/src/jvmMain/kotlin/hep/dataforge/vis/spatial/geometryJVM.kt @@ -1,12 +1,13 @@ package hep.dataforge.vis.spatial +import org.fxyz3d.geometry.Point3D as FXPoint3D actual data class Point2D(actual var x: Double, actual var y: Double) { actual constructor(x: Number, y: Number) : this(x.toDouble(), y.toDouble()) } -actual class Point3D(val point: org.fxyz3d.geometry.Point3D) { +actual class Point3D(val point: FXPoint3D) { actual constructor(x: Number, y: Number, z: Number) : this( - org.fxyz3d.geometry.Point3D( + FXPoint3D( x.toFloat(), y.toFloat(), z.toFloat() @@ -32,7 +33,7 @@ actual class Point3D(val point: org.fxyz3d.geometry.Point3D) { } override fun equals(other: Any?): Boolean { - return this.point == (other as? hep.dataforge.vis.spatial.Point3D)?.point + return this.point == (other as? Point3D)?.point } override fun hashCode(): Int { diff --git a/demo/gdml/build.gradle.kts b/demo/gdml/build.gradle.kts index 67570e1d..7558d178 100644 --- a/demo/gdml/build.gradle.kts +++ b/demo/gdml/build.gradle.kts @@ -27,6 +27,11 @@ kotlin { api(project(":dataforge-vis-spatial-gdml")) } } + jvmMain{ + dependencies { + api("org.fxyz3d:fxyz3d:0.5.2") + } + } } } diff --git a/demo/gdml/src/commonMain/kotlin/hep/dataforge/vis/spatial/gdml/demo/cubes.kt b/demo/gdml/src/commonMain/kotlin/hep/dataforge/vis/spatial/gdml/demo/cubes.kt new file mode 100644 index 00000000..24e4845b --- /dev/null +++ b/demo/gdml/src/commonMain/kotlin/hep/dataforge/vis/spatial/gdml/demo/cubes.kt @@ -0,0 +1,54 @@ +package hep.dataforge.vis.spatial.gdml.demo + +import scientifik.gdml.* + + +fun cubes(): GDML = GDML { + val center = define.position("center") + structure { + val air = ref("G4_AIR") + val tubeMaterial = ref("tube") + val boxMaterial = ref("box") + val segment = solids.tube("segment", 20, 5.0) { + rmin = 17 + deltaphi = 60 + aunit = DEG + } + val worldBox = solids.box("LargeBox", 200, 200, 200) + val smallBox = solids.box("smallBox", 30, 30, 30) + val segmentVolume = volume("segment", tubeMaterial, segment.ref()) {} + val circle = volume("composite", boxMaterial, smallBox.ref()) { + for (i in 0 until 6) { + physVolume(segmentVolume) { + name = "segment$i" + positionref = center.ref() + rotation { + z = 60 * i + unit = DEG + } + } + } + } + world = volume("world", air, worldBox.ref()) { + for (i in 0 until 3) { + for (j in 0 until 3) { + for (k in 0 until 3) { + physVolume(circle) { + name = "composite$i$j$k" + position { + x = (-50 + i * 50) + y = (-50 + j * 50) + z = (-50 + k * 50) + } + rotation { + x = i * 120 + y = j * 120 + z = 120 * k + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/demo/gdml/src/jsMain/resources/cubes.gdml b/demo/gdml/src/commonMain/resources/cubes.gdml similarity index 100% rename from demo/gdml/src/jsMain/resources/cubes.gdml rename to demo/gdml/src/commonMain/resources/cubes.gdml diff --git a/demo/gdml/src/commonTest/kotlin/hep/dataforge/vis/spatial/gdml/GDMLVisualTest.kt b/demo/gdml/src/commonTest/kotlin/hep/dataforge/vis/spatial/gdml/GDMLVisualTest.kt new file mode 100644 index 00000000..bac4481e --- /dev/null +++ b/demo/gdml/src/commonTest/kotlin/hep/dataforge/vis/spatial/gdml/GDMLVisualTest.kt @@ -0,0 +1,18 @@ +package hep.dataforge.vis.spatial.gdml + +import hep.dataforge.meta.string +import hep.dataforge.names.toName +import hep.dataforge.vis.spatial.Material3D +import hep.dataforge.vis.spatial.gdml.demo.cubes +import kotlin.test.Test +import kotlin.test.assertEquals + +class GDMLVisualTest { + @Test + fun testPrototypeProperty() { + val gdml = cubes() + val visual = gdml.toVisual() + visual["composite000.segment0".toName()]?.setProperty(Material3D.MATERIAL_COLOR_KEY, "red") + assertEquals("red", visual["composite000.segment0".toName()]?.getProperty(Material3D.MATERIAL_COLOR_KEY).string) + } +} \ No newline at end of file diff --git a/demo/gdml/src/jsMain/kotlin/hep/dataforge/vis/spatial/gdml/demo/GDMLDemoApp.kt b/demo/gdml/src/jsMain/kotlin/hep/dataforge/vis/spatial/gdml/demo/GDMLDemoApp.kt index f4e6d4c5..88a948f3 100644 --- a/demo/gdml/src/jsMain/kotlin/hep/dataforge/vis/spatial/gdml/demo/GDMLDemoApp.kt +++ b/demo/gdml/src/jsMain/kotlin/hep/dataforge/vis/spatial/gdml/demo/GDMLDemoApp.kt @@ -14,7 +14,6 @@ import hep.dataforge.vis.js.editor.displayPropertyEditor import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_COLOR_KEY import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_OPACITY_KEY import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_WIREFRAME_KEY -import hep.dataforge.vis.spatial.Visual3DPlugin import hep.dataforge.vis.spatial.VisualGroup3D import hep.dataforge.vis.spatial.VisualObject3D import hep.dataforge.vis.spatial.VisualObject3D.Companion.VISIBLE_KEY @@ -25,11 +24,7 @@ import hep.dataforge.vis.spatial.three.ThreePlugin import hep.dataforge.vis.spatial.three.displayCanvasControls import hep.dataforge.vis.spatial.three.output import hep.dataforge.vis.spatial.visible -import kotlinx.html.dom.append -import kotlinx.html.js.p -import org.w3c.dom.DragEvent -import org.w3c.dom.HTMLDivElement -import org.w3c.dom.HTMLElement +import org.w3c.dom.* import org.w3c.files.FileList import org.w3c.files.FileReader import org.w3c.files.get @@ -81,17 +76,18 @@ private class GDMLDemoApp : Application { } private fun message(message: String?) { - document.getElementById("messages")?.let { element -> - if (message == null) { - element.clear() - } else { - element.append { - p { - +message - } - } - } - } + console.log(message) +// document.getElementById("messages")?.let { element -> +// if (message == null) { +// element.clear() +// } else { +// element.append { +// p { +// +message +// } +// } +// } +// } } private val gdmlConfiguration: GDMLTransformer.() -> Unit = { @@ -128,7 +124,7 @@ private class GDMLDemoApp : Application { //val url = URL("https://drive.google.com/open?id=1w5e7fILMN83JGgB8WANJUYm8OW2s0WVO") val canvasElement = document.getElementById("canvas") ?: error("Element with id 'canvas' not found on page") - val configElement = document.getElementById("layers") ?: error("Element with id 'layers' not found on page") + val configElement = document.getElementById("config") ?: error("Element with id 'layers' not found on page") val treeElement = document.getElementById("tree") ?: error("Element with id 'tree' not found on page") val editorElement = document.getElementById("editor") ?: error("Element with id 'editor' not found on page") canvasElement.clear() @@ -136,15 +132,14 @@ private class GDMLDemoApp : Application { val action: (name: String, data: String) -> Unit = { name, data -> canvasElement.clear() spinner(true) - message("Loading GDML") - val gdml = GDML.format.parse(GDML.serializer(), data) - message("Converting GDML into DF-VIS format") - val visual: VisualObject3D = when { - name.endsWith(".gdml") || name.endsWith(".xml") -> gdml.toVisual(gdmlConfiguration) - name.endsWith(".json") -> { - Visual3DPlugin.json.parse(VisualGroup3D.serializer(), data).apply { attachChildren() } + name.endsWith(".gdml") || name.endsWith(".xml") -> { + message("Loading GDML") + val gdml = GDML.format.parse(GDML.serializer(), data) + message("Converting GDML into DF-VIS format") + gdml.toVisual(gdmlConfiguration) } + name.endsWith(".json") -> VisualGroup3D.fromJson(data) else -> { window.alert("File extension is not recognized: $name") error("File extension is not recognized: $name") @@ -187,9 +182,9 @@ private class GDMLDemoApp : Application { // canvas.clickListener = ::selectElement //tree.visualObjectTree(visual, editor::propertyEditor) - treeElement.displayObjectTree(visual) { name -> - selectElement(name) - canvas.highlight(name) + treeElement.displayObjectTree(visual) { treeName -> + selectElement(treeName) + canvas.highlight(treeName) } canvas.render(visual) @@ -204,6 +199,19 @@ private class GDMLDemoApp : Application { addEventListener("dragover", { handleDragOver(it as DragEvent) }, false) addEventListener("drop", { loadData(it as DragEvent, action) }, false) } + (document.getElementById("file_load_button") as? HTMLInputElement)?.apply { + addEventListener("change", { + (it.target as HTMLInputElement).files?.asList()?.first()?.let { file -> + FileReader().apply { + onload = { + val string = result as String + action(file.name, string) + } + readAsText(file) + } + } + }, false) + } } } diff --git a/demo/gdml/src/jsMain/web/css/main.css b/demo/gdml/src/jsMain/web/css/main.css index e7f925df..ae59b43e 100644 --- a/demo/gdml/src/jsMain/web/css/main.css +++ b/demo/gdml/src/jsMain/web/css/main.css @@ -1,3 +1,7 @@ +.drop_zone { + outline: 1px solid orange; +} + .loader { border: 16px solid #f3f3f3; /* Light grey */ border-top: 16px solid #3498db; /* Blue */ diff --git a/demo/gdml/src/jsMain/web/index.html b/demo/gdml/src/jsMain/web/index.html index bad59418..6a8a8ebe 100644 --- a/demo/gdml/src/jsMain/web/index.html +++ b/demo/gdml/src/jsMain/web/index.html @@ -4,34 +4,45 @@ Three js demo for particle physics - - - + + + -
- Load data -
- (drag file here) -
+

GDML demo

- -
-
-
-
-
-
-
+
+
+
+
+
+ Load file: + +
+
+
+
+ Load data +
+ (drag file here) +
+
+
+
+
+
+
+
+
+
-
- + + + \ No newline at end of file diff --git a/demo/gdml/src/jvmMain/kotlin/hep/dataforge/vis/spatial/gdml/demo/GDMLDemoApp.kt b/demo/gdml/src/jvmMain/kotlin/hep/dataforge/vis/spatial/gdml/demo/GDMLDemoApp.kt index b4a75c37..ff07ebc6 100644 --- a/demo/gdml/src/jvmMain/kotlin/hep/dataforge/vis/spatial/gdml/demo/GDMLDemoApp.kt +++ b/demo/gdml/src/jvmMain/kotlin/hep/dataforge/vis/spatial/gdml/demo/GDMLDemoApp.kt @@ -4,15 +4,13 @@ import hep.dataforge.context.Global import hep.dataforge.vis.fx.editor.VisualObjectEditorFragment import hep.dataforge.vis.fx.editor.VisualObjectTreeFragment import hep.dataforge.vis.spatial.Material3D +import hep.dataforge.vis.spatial.Visual3D +import hep.dataforge.vis.spatial.VisualGroup3D import hep.dataforge.vis.spatial.fx.FX3DPlugin import hep.dataforge.vis.spatial.fx.FXCanvas3D -import hep.dataforge.vis.spatial.gdml.LUnit -import hep.dataforge.vis.spatial.gdml.readFile -import hep.dataforge.vis.spatial.gdml.toVisual import javafx.geometry.Orientation import javafx.scene.Parent import javafx.stage.FileChooser -import scientifik.gdml.GDML import tornadofx.* class GDMLDemoApp : App(GDMLView::class) @@ -36,26 +34,12 @@ class GDMLView : View() { override val root: Parent = borderpane { top { buttonbar { - button("Load GDML") { + button("Load GDML/json") { action { - val file = chooseFile("Select a GDML file", filters = gdmlFilter).firstOrNull() - if (file != null) { - val obj = GDML.readFile(file.toPath()).toVisual { - lUnit = LUnit.CM - - solidConfiguration = { parent, solid -> - if (solid.name == "cave") { - setProperty(Material3D.MATERIAL_WIREFRAME_KEY, true) - } - if (parent.physVolumes.isNotEmpty()) { - useStyle("opaque") { - Material3D.MATERIAL_OPACITY_KEY put 0.3 - } - } - } - } - canvas.render(obj) - } + val file = chooseFile("Select a GDML/json file", filters = fileNameFilter).firstOrNull() + ?: return@action + val visual: VisualGroup3D = Visual3D.readFile(file) + canvas.render(visual) } } } @@ -68,8 +52,11 @@ class GDMLView : View() { } companion object { - private val gdmlFilter = arrayOf( - FileChooser.ExtensionFilter("GDML", "*.gdml", "*.xml") + private val fileNameFilter = arrayOf( + FileChooser.ExtensionFilter("GDML", "*.gdml", "*.xml"), + FileChooser.ExtensionFilter("JSON", "*.json"), + FileChooser.ExtensionFilter("JSON.ZIP", "*.json.zip"), + FileChooser.ExtensionFilter("JSON.GZ", "*.json.gz") ) } } diff --git a/demo/gdml/src/jvmMain/kotlin/hep/dataforge/vis/spatial/gdml/demo/readFile.kt b/demo/gdml/src/jvmMain/kotlin/hep/dataforge/vis/spatial/gdml/demo/readFile.kt new file mode 100644 index 00000000..532e1399 --- /dev/null +++ b/demo/gdml/src/jvmMain/kotlin/hep/dataforge/vis/spatial/gdml/demo/readFile.kt @@ -0,0 +1,49 @@ +package hep.dataforge.vis.spatial.gdml.demo + +import hep.dataforge.vis.spatial.Material3D +import hep.dataforge.vis.spatial.Visual3D +import hep.dataforge.vis.spatial.VisualGroup3D +import hep.dataforge.vis.spatial.gdml.LUnit +import hep.dataforge.vis.spatial.gdml.readFile +import hep.dataforge.vis.spatial.gdml.toVisual +import scientifik.gdml.GDML +import java.io.File +import java.util.zip.GZIPInputStream +import java.util.zip.ZipInputStream + +fun Visual3D.Companion.readFile(file: File): VisualGroup3D = when { + file.extension == "gdml" || file.extension == "xml" -> { + GDML.readFile(file.toPath()).toVisual { + lUnit = LUnit.CM + + solidConfiguration = { parent, solid -> + if (solid.name == "cave") { + setProperty(Material3D.MATERIAL_WIREFRAME_KEY, true) + } + if (parent.physVolumes.isNotEmpty()) { + useStyle("opaque") { + Material3D.MATERIAL_OPACITY_KEY put 0.3 + } + } + } + } + } + file.extension == "json" -> VisualGroup3D.fromJson(file.readText()) + file.name.endsWith("json.zip") -> { + file.inputStream().use { + val unzip = ZipInputStream(it, Charsets.UTF_8) + val text = unzip.readAllBytes().decodeToString() + VisualGroup3D.fromJson(text) + } + } + file.name.endsWith("json.gz") -> { + file.inputStream().use { + val unzip = GZIPInputStream(it) + val text = unzip.readAllBytes().decodeToString() + VisualGroup3D.fromJson(text) + } + } + else -> error("Unknown extension ${file.extension}") +} + +fun Visual3D.Companion.readFile(fileName: String): VisualGroup3D = readFile(File(fileName)) \ No newline at end of file diff --git a/demo/gdml/src/jvmMain/kotlin/hep/dataforge/vis/spatial/gdml/demo/saveToJson.kt b/demo/gdml/src/jvmMain/kotlin/hep/dataforge/vis/spatial/gdml/demo/saveToJson.kt new file mode 100644 index 00000000..f2491423 --- /dev/null +++ b/demo/gdml/src/jvmMain/kotlin/hep/dataforge/vis/spatial/gdml/demo/saveToJson.kt @@ -0,0 +1,19 @@ +package hep.dataforge.vis.spatial.gdml.demo + +import hep.dataforge.vis.spatial.Visual3D +import hep.dataforge.vis.spatial.VisualGroup3D +import hep.dataforge.vis.spatial.gdml.LUnit +import hep.dataforge.vis.spatial.gdml.readFile +import hep.dataforge.vis.spatial.gdml.toVisual +import scientifik.gdml.GDML +import java.io.File +import java.nio.file.Paths + +fun main() { + val gdml = GDML.readFile(Paths.get("D:\\Work\\Projects\\gdml.kt\\gdml-source\\cubes.gdml")) + val visual = gdml.toVisual { + lUnit = LUnit.CM + } + val json = Visual3D.json.stringify(VisualGroup3D.serializer(), visual) + File("D:\\Work\\Projects\\gdml.kt\\gdml-source\\cubes.json").writeText(json) +} \ No newline at end of file diff --git a/demo/gdml/src/jvmTest/kotlin/hep/dataforge/vis/spatial/FileSerializationTest.kt b/demo/gdml/src/jvmTest/kotlin/hep/dataforge/vis/spatial/FileSerializationTest.kt new file mode 100644 index 00000000..273c25ba --- /dev/null +++ b/demo/gdml/src/jvmTest/kotlin/hep/dataforge/vis/spatial/FileSerializationTest.kt @@ -0,0 +1,15 @@ +package hep.dataforge.vis.spatial + +import hep.dataforge.names.asName +import org.junit.Test +import kotlin.test.Ignore + +class FileSerializationTest { + @Test + @Ignore + fun testFileRead(){ + val text = this::class.java.getResourceAsStream("/cubes.json").readAllBytes().decodeToString() + val visual = VisualGroup3D.fromJson(text) + visual["composite_001".asName()] + } +} \ No newline at end of file diff --git a/demo/muon-monitor/src/jsMain/kotlin/ru/mipt/npm/muon/monitor/MMDemoApp.kt b/demo/muon-monitor/src/jsMain/kotlin/ru/mipt/npm/muon/monitor/MMDemoApp.kt index 83c3f4d1..3e680952 100644 --- a/demo/muon-monitor/src/jsMain/kotlin/ru/mipt/npm/muon/monitor/MMDemoApp.kt +++ b/demo/muon-monitor/src/jsMain/kotlin/ru/mipt/npm/muon/monitor/MMDemoApp.kt @@ -15,7 +15,7 @@ import hep.dataforge.vis.js.editor.displayPropertyEditor import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_COLOR_KEY import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_OPACITY_KEY import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_WIREFRAME_KEY -import hep.dataforge.vis.spatial.Visual3DPlugin +import hep.dataforge.vis.spatial.Visual3D import hep.dataforge.vis.spatial.VisualObject3D import hep.dataforge.vis.spatial.VisualObject3D.Companion.VISIBLE_KEY import hep.dataforge.vis.spatial.three.ThreePlugin @@ -35,13 +35,13 @@ import org.w3c.dom.HTMLElement import kotlin.browser.document import kotlin.dom.clear -private class GDMLDemoApp : Application { +private class MMDemoApp : Application { private val model = Model() private val connection = HttpClient { install(JsonFeature) { - serializer = KotlinxSerializer(Visual3DPlugin.json) + serializer = KotlinxSerializer(Visual3D.json) } } @@ -122,5 +122,5 @@ private class GDMLDemoApp : Application { } fun main() { - startApplication(::GDMLDemoApp) + startApplication(::MMDemoApp) } \ No newline at end of file diff --git a/demo/muon-monitor/src/jvmMain/kotlin/ru/mipt/npm/muon/monitor/server/MMServer.kt b/demo/muon-monitor/src/jvmMain/kotlin/ru/mipt/npm/muon/monitor/server/MMServer.kt index 1518aa35..f46f68e1 100644 --- a/demo/muon-monitor/src/jvmMain/kotlin/ru/mipt/npm/muon/monitor/server/MMServer.kt +++ b/demo/muon-monitor/src/jvmMain/kotlin/ru/mipt/npm/muon/monitor/server/MMServer.kt @@ -1,7 +1,7 @@ package ru.mipt.npm.muon.monitor.server -import hep.dataforge.vis.spatial.Visual3DPlugin +import hep.dataforge.vis.spatial.Visual3D import io.ktor.application.Application import io.ktor.application.call import io.ktor.application.install @@ -35,7 +35,7 @@ fun Application.module() { install(DefaultHeaders) install(CallLogging) install(ContentNegotiation) { - serialization(json = Visual3DPlugin.json) + serialization(json = Visual3D.json) } install(Routing) { get("/event") { diff --git a/demo/spatial-showcase/src/jsMain/kotlin/hep/dataforge/vis/spatial/demo/VariableBox.kt b/demo/spatial-showcase/src/jsMain/kotlin/hep/dataforge/vis/spatial/demo/VariableBox.kt index 20540777..63e6c975 100644 --- a/demo/spatial-showcase/src/jsMain/kotlin/hep/dataforge/vis/spatial/demo/VariableBox.kt +++ b/demo/spatial-showcase/src/jsMain/kotlin/hep/dataforge/vis/spatial/demo/VariableBox.kt @@ -7,6 +7,7 @@ import hep.dataforge.meta.number import hep.dataforge.names.plus import hep.dataforge.names.startsWith import hep.dataforge.vis.common.getProperty +import hep.dataforge.vis.common.set import hep.dataforge.vis.common.setProperty import hep.dataforge.vis.spatial.* import hep.dataforge.vis.spatial.VisualObject3D.Companion.GEOMETRY_KEY diff --git a/doc/resources/class-diag-3d.png b/doc/resources/class-diag-3d.png new file mode 100644 index 00000000..4c9af2cd Binary files /dev/null and b/doc/resources/class-diag-3d.png differ diff --git a/doc/resources/class-diag-common.png b/doc/resources/class-diag-common.png new file mode 100644 index 00000000..4943c3f4 Binary files /dev/null and b/doc/resources/class-diag-common.png differ diff --git a/doc/resources/gdml-demo.png b/doc/resources/gdml-demo.png new file mode 100644 index 00000000..4498ddcd Binary files /dev/null and b/doc/resources/gdml-demo.png differ diff --git a/doc/resources/muon-monitor.png b/doc/resources/muon-monitor.png new file mode 100644 index 00000000..a5a4f154 Binary files /dev/null and b/doc/resources/muon-monitor.png differ diff --git a/doc/resources/spatial-showcase.png b/doc/resources/spatial-showcase.png new file mode 100644 index 00000000..5df95df9 Binary files /dev/null and b/doc/resources/spatial-showcase.png differ