diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..e69de29b diff --git a/README.md b/README.md index 51eba421..35c4daca 100644 --- a/README.md +++ b/README.md @@ -2,19 +2,19 @@ # DataForge Visualization Platform -## Table of contents +## Table of Contents * [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) + * [visionforge-core](#visionforge-core) + * [visionforge-solid](#visionforge-solid) + * [visionforge-gdml](#visionforge-gdml) +* [Visualization for External Systems](#visualization-for-external-systems) * [Demonstrations](#demonstrations) - * [Spatial Showcase](#spatial-showcase) - * [Muon Monitor](#muon-monitor-visualization) + * [Simple Example - Spatial Showcase](#simple-example---spatial-showcase) + * [Full-Stack Application Example - Muon Monitor](#full-stack-application-example---muon-monitor-visualization) * [GDML Example](#gdml-example) @@ -26,8 +26,8 @@ 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. +The project is developed as a [Kotlin multiplatform](https://kotlinlang.org/docs/reference/multiplatform.html) +application, currently targeting browser JavaScript and JVM. ## Features @@ -41,7 +41,6 @@ The main framework's features for now include: - Settings export and import - Multiple platform support - ## About DataForge DataForge is a software framework for automated scientific data processing. DataForge Visualization @@ -56,25 +55,25 @@ To learn more about DataForge, please consult the following URLs: ## Modules contained in this repository -### dataforge-vis-common +### visionforge-core 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`). +The `visionforge-core` module also includes configuration editors for JS (in `jsMain`) and JVM (in `jvmMain`). -##### Class diagram: +**Class diagram:** -![](doc/resources/class-diag-common.png) +![](doc/resources/class-diag-core.png) -### dataforge-vis-spatial +### visionforge-solid -Includes common classes and serializers for 3D visualization, Three.js and JavaFX implementations. +Includes common classes and serializers for 3D visualization, as well as Three.js and JavaFX implementations. -##### Class diagram: +**Class diagram:** -![](doc/resources/class-diag-3d.png) +![](doc/resources/class-diag-solid.png) ##### Prototypes @@ -82,65 +81,63 @@ One of the important features of the framework is support for 3D object prototyp 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. +The `prototypes` property tree is defined in `SolidGroup` class via `PrototypeHolder` interface, 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`. +`SolidGroup` has a `styleSheet` property that can optionally define styles at the Group's +level. Styles are applied to child (descendant) objects using `Vision.styles: List` property. -### dataforge-vis-spatial-gdml +### visionforge-gdml GDML bindings for 3D visualization (to be moved to gdml project). -### dataforge-vis-jsroot - -Some JSROOT bindings. - -Note: Currently, this part is experimental and put here for completeness. This module may not build. +## Visualization for External Systems +The `visionforge` framework can be used to visualize geometry and events from external, +non-Kotlin based systems, such as ROOT. This will require a plugin to convert data model +of the external system to that of `visionforge`. Performing such integration is a work +currently in progress. + ## Demonstrations -The `demo` module contains several demonstrations of using the `dataforge-vis` framework: +The `demo` module contains several example projects (demonstrations) of using the `visionforge` framework. +They are briefly described in this section, for more details please consult the corresponding per-project +README file. -### Spatial Showcase +### Simple Example - Spatial Showcase 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/Tasks/distribution/installJsDist` Gradle task, then open -`build/distribuions/spatial-showcase-js-0.1.0-dev/index.html` file in your browser. +Some shapes will also periodically change their color and visibility. -##### Example view: +[More details](demo/spatial-showcase/README.md) + +**Example view:** ![](doc/resources/spatial-showcase.png) -### Muon Monitor Visualization + +### Full-Stack Application Example - Muon Monitor Visualization A full-stack application example, showing the -[Muon Monitor](http://npm.mipt.ru/projects/physics.html#mounMonitor) experiment set-up. +[Muon Monitor](http://npm.mipt.ru/en/projects/physics#mounMonitor) experiment set-up. -Includes server back-end generating events, as well as visualization front-end. +[More details](demo/muon-monitor/README.md) -To run full-stack app (both server and browser front-end), run -`demo/muon-monitor/application/run` task. - -##### Example view: +**Example view:** ![](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`. +[More details](demo/gdml/README.md) ##### Example view: diff --git a/build.gradle.kts b/build.gradle.kts index b7ee4ffd..56f0b451 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,15 +1,14 @@ -import scientifik.fx -import scientifik.serialization +import scientifik.useFx +import scientifik.useSerialization -val dataforgeVersion by extra("0.1.7") +val dataforgeVersion by extra("0.1.8") plugins { - val toolsVersion = "0.4.2" - id("scientifik.mpp") version toolsVersion apply false - id("scientifik.jvm") version toolsVersion apply false - id("scientifik.js") version toolsVersion apply false - id("scientifik.publish") version toolsVersion apply false - id("org.openjfx.javafxplugin") version "0.0.8" apply false + id("scientifik.mpp") apply false + id("scientifik.jvm") apply false + id("scientifik.js") apply false + id("scientifik.publish") apply false + id("org.jetbrains.changelog") version "0.4.0" } allprojects { @@ -17,24 +16,20 @@ allprojects { mavenLocal() maven("https://dl.bintray.com/pdvrieze/maven") maven("http://maven.jzy3d.org/releases") - maven("https://kotlin.bintray.com/js-externals") - maven("https://kotlin.bintray.com/kotlin-js-wrappers/") -// maven("https://dl.bintray.com/gbaldeck/kotlin") -// maven("https://dl.bintray.com/rjaros/kotlin") } group = "hep.dataforge" - version = "0.1.3-dev" + version = "0.1.5-dev" } -val githubProject by extra("dataforge-vis") +val githubProject by extra("visionforge") val bintrayRepo by extra("dataforge") val fxVersion by extra("14") subprojects { - apply(plugin = "scientifik.publish") - serialization() - afterEvaluate { - fx(scientifik.FXModule.CONTROLS, version = fxVersion) + if(name.startsWith("visionforge")) { + apply(plugin = "scientifik.publish") } + useSerialization() + useFx(scientifik.FXModule.CONTROLS, version = fxVersion) } \ No newline at end of file diff --git a/dataforge-vis-common/build.gradle.kts b/dataforge-vis-common/build.gradle.kts deleted file mode 100644 index 993c616e..00000000 --- a/dataforge-vis-common/build.gradle.kts +++ /dev/null @@ -1,51 +0,0 @@ -plugins { - id("scientifik.mpp") -} - -val dataforgeVersion: String by rootProject.extra -//val kvisionVersion: String by rootProject.extra("2.0.0-M1") - -kotlin { - sourceSets { - commonMain { - dependencies { - api("hep.dataforge:dataforge-output:$dataforgeVersion") - } - } - jvmMain { - dependencies { - api("no.tornado:tornadofx:1.7.20") - //api("no.tornado:tornadofx-controlsfx:0.1.1") - api("de.jensd:fontawesomefx-fontawesome:4.7.0-11") { - exclude(group = "org.openjfx") - } - api("de.jensd:fontawesomefx-commons:11.0") { - exclude(group = "org.openjfx") - } - } - } - jsMain { - dependencies { - api("hep.dataforge:dataforge-output-html:$dataforgeVersion") - - //React, React DOM + Wrappers (chapter 3) - api("org.jetbrains:kotlin-react:16.13.0-pre.94-kotlin-1.3.70") - api("org.jetbrains:kotlin-react-dom:16.13.0-pre.94-kotlin-1.3.70") - api(npm("react", "16.13.0")) - api(npm("react-dom", "16.13.0")) - - //Kotlin Styled (chapter 3) - api("org.jetbrains:kotlin-styled:1.0.0-pre.94-kotlin-1.3.70") - api(npm("styled-components")) - api(npm("inline-style-prefixer")) - - api(npm("source-map-resolve","0.6.0")) - api(npm("bootstrap","4.3.1")) - api(npm("popper.js","1.14.7")) - api(npm("jquery","3.5.0")) - //api(npm("jsoneditor", "8.6.1")) - api(npm("file-saver","2.0.2")) - } - } - } -} \ No newline at end of file diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/AbstractVisualObject.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/AbstractVisualObject.kt deleted file mode 100644 index e355fa94..00000000 --- a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/AbstractVisualObject.kt +++ /dev/null @@ -1,97 +0,0 @@ -package hep.dataforge.vis - -import hep.dataforge.meta.* -import hep.dataforge.names.Name -import hep.dataforge.names.asName -import hep.dataforge.values.Value -import hep.dataforge.vis.VisualObject.Companion.STYLE_KEY -import kotlinx.serialization.Transient - -internal data class PropertyListener( - val owner: Any? = null, - val action: (name: Name, oldItem: MetaItem<*>?, newItem: MetaItem<*>?) -> Unit -) - -abstract class AbstractVisualObject : VisualObject { - - @Transient - override var parent: VisualGroup? = null - - protected abstract var properties: Config? - - override var styles: List - get() = properties?.get(STYLE_KEY).stringList - set(value) { - //val allStyles = (field + value).distinct() - setProperty(STYLE_KEY, Value.of(value)) - updateStyles(value) - } - - protected fun updateStyles(names: List) { - names.mapNotNull { findStyle(it) }.asSequence() - .flatMap { it.items.asSequence() } - .distinctBy { it.key } - .forEach { - propertyChanged(it.key.asName(), null, it.value) - } - } - - /** - * The config is initialized and assigned on-demand. - * To avoid unnecessary allocations, one should access [properties] via [getProperty] instead. - */ - override val config: Config - get() = properties ?: Config().also { config -> - properties = config.apply { onChange(this, ::propertyChanged) } - } - - @Transient - private val listeners = HashSet() - - override fun propertyChanged(name: Name, before: MetaItem<*>?, after: MetaItem<*>?) { - if (before != after) { - for (l in listeners) { - l.action(name, before, after) - } - } - } - - override fun onPropertyChange(owner: Any?, action: (Name, before: MetaItem<*>?, after: MetaItem<*>?) -> Unit) { - listeners.add(PropertyListener(owner, action)) - } - - override fun removeChangeListener(owner: Any?) { - listeners.removeAll { it.owner == owner } - } - - private var styleCache: Meta? = null - - /** - * Collect all styles for this object in a single cached meta - */ - protected val mergedStyles: Meta - get() = styleCache ?: findAllStyles().merge().also { - styleCache = it - } - - /** - * All available properties in a layered form - */ - override fun allProperties(): Laminate = Laminate(properties, mergedStyles) - - override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? { - return if (inherit) { - properties?.get(name) ?: mergedStyles[name] ?: parent?.getProperty(name, inherit) - } else { - properties?.get(name) ?: mergedStyles[name] - } - } -} - -//fun VisualObject.findStyle(styleName: Name): Meta? { -// if (this is VisualGroup) { -// val style = resolveStyle(styleName) -// if (style != null) return style -// } -// return parent?.findStyle(styleName) -//} \ No newline at end of file diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/Visual.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/Visual.kt deleted file mode 100644 index 8aeb32a4..00000000 --- a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/Visual.kt +++ /dev/null @@ -1,39 +0,0 @@ -package hep.dataforge.vis - -import hep.dataforge.context.* -import hep.dataforge.meta.Meta -import kotlin.reflect.KClass - -interface VisualFactory { - val type: KClass - operator fun invoke( - context: Context, - parent: VisualObject?, - meta: Meta - ): T -} - -class Visual(meta: Meta) : AbstractPlugin(meta) { - override val tag: PluginTag get() = Companion.tag - - /** - * Create a list of factories on first call and cache it - */ - val visualFactories by lazy { - context.content>(VISUAL_FACTORY_TYPE).mapKeys { it.value.type } - } - - inline fun buildVisual(parent: VisualObject?, meta: Meta): T? { - return visualFactories[T::class]?.invoke(context, parent, meta) as T? - } - - companion object : PluginFactory { - override val tag: PluginTag = PluginTag(name = "visual", group = PluginTag.DATAFORGE_GROUP) - override val type: KClass = Visual::class - - override fun invoke(meta: Meta, context: Context): Visual = - Visual(meta) - - const val VISUAL_FACTORY_TYPE = "visual.factory" - } -} \ No newline at end of file diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/VisualObject.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/VisualObject.kt deleted file mode 100644 index b757fcb3..00000000 --- a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/VisualObject.kt +++ /dev/null @@ -1,121 +0,0 @@ -package hep.dataforge.vis - -import hep.dataforge.meta.Configurable -import hep.dataforge.meta.Laminate -import hep.dataforge.meta.Meta -import hep.dataforge.meta.MetaItem -import hep.dataforge.names.Name -import hep.dataforge.names.asName -import hep.dataforge.names.toName -import hep.dataforge.provider.Type -import hep.dataforge.vis.VisualObject.Companion.TYPE -import kotlinx.serialization.PolymorphicSerializer -import kotlinx.serialization.Transient - -//private fun Laminate.withTop(meta: Meta): Laminate = Laminate(listOf(meta) + layers) -//private fun Laminate.withBottom(meta: Meta): Laminate = Laminate(layers + meta) - -/** - * A root type for display hierarchy - */ -@Type(TYPE) -interface VisualObject : Configurable { - - /** - * The parent object of this one. If null, this one is a root. - */ - @Transient - var parent: VisualGroup? - - /** - * All properties including styles and prototypes if present, but without inheritance - */ - fun allProperties(): Laminate - - /** - * Get property including or excluding parent properties - */ - fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? - - override fun getProperty(name: Name): MetaItem<*>? = getProperty(name, true) - - /** - * Trigger property invalidation event. If [name] is empty, notify that the whole object is changed - */ - fun propertyChanged(name: Name, before: MetaItem<*>?, after: MetaItem<*>?): Unit - - fun propertyInvalidated(name: Name) = propertyChanged(name, null, null) - - /** - * Add listener triggering on property change - */ - fun onPropertyChange(owner: Any?, action: (Name, before: MetaItem<*>?, after: MetaItem<*>?) -> Unit): Unit - - /** - * Remove change listeners with given owner. - */ - fun removeChangeListener(owner: Any?) - - /** - * List of names of styles applied to this object. Order matters. Not inherited - */ - var styles: List - - companion object { - const val TYPE = "visual" - val STYLE_KEY = "@style".asName() - - private val VISUAL_OBJECT_SERIALIZER = PolymorphicSerializer(VisualObject::class) - - fun serializer() = VISUAL_OBJECT_SERIALIZER - - //const val META_KEY = "@meta" - //const val TAGS_KEY = "@tags" - - } -} - -/** - * Get [VisualObject] property using key as a String - */ -fun VisualObject.getProperty(key: String, inherit: Boolean = true): MetaItem<*>? = getProperty(key.toName(), inherit) - -/** - * Add style name to the list of styles to be resolved later. The style with given name does not necessary exist at the moment. - */ -fun VisualObject.useStyle(name: String) { - styles = styles + name -} - -//private tailrec fun VisualObject.topGroup(): VisualGroup? { -// val parent = this.parent -// return if (parent == null) { -// this as? VisualGroup -// } -// else { -// parent.topGroup() -// } -//} -// -///** -// * Add or update given style on a top-most reachable parent group and apply it to this object -// */ -//fun VisualObject.useStyle(name: String, builder: MetaBuilder.() -> Unit) { -// val styleName = name.toName() -// topGroup()?.updateStyle(styleName, builder) ?: error("Can't find parent group for $this") -// useStyle(styleName) -//} - -tailrec fun VisualObject.findStyle(name: String): Meta? = - (this as? VisualGroup)?.styleSheet?.get(name) ?: parent?.findStyle(name) - -fun VisualObject.findAllStyles(): Laminate = Laminate(styles.mapNotNull(::findStyle)) - -//operator fun VisualObject.get(name: Name): VisualObject?{ -// return when { -// name.isEmpty() -> this -// this is VisualGroup -> this[name] -// else -> null -// } -//} - diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/VisualObjectDelegate.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/VisualObjectDelegate.kt deleted file mode 100644 index d0798522..00000000 --- a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/VisualObjectDelegate.kt +++ /dev/null @@ -1,131 +0,0 @@ -package hep.dataforge.vis - -import hep.dataforge.meta.* -import hep.dataforge.names.Name -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 - - -/** - * A delegate for display object properties - */ -class VisualObjectDelegate( - val key: Name?, - val default: MetaItem<*>?, - val inherited: Boolean -) : ReadWriteProperty?> { - override fun getValue(thisRef: VisualObject, property: KProperty<*>): MetaItem<*>? { - val name = key ?: property.name.asName() - return if (inherited) { - thisRef.getProperty(name) - } else { - thisRef.config[name] - } ?: default - } - - override fun setValue(thisRef: VisualObject, property: KProperty<*>, value: MetaItem<*>?) { - val name = key ?: property.name.asName() - thisRef.config[name] = value - } -} - -class VisualObjectDelegateWrapper( - val obj: VisualObject, - val key: Name?, - val default: T, - val inherited: Boolean, - val write: Config.(name: Name, value: T) -> Unit = { name, value -> set(name, value) }, - val read: (MetaItem<*>?) -> T? -) : ReadWriteProperty { - - //private var cachedName: Name? = null - - override fun getValue(thisRef: Any?, property: KProperty<*>): T { - val name = key ?: property.name.asName() - return read(obj.getProperty(name, inherited)) ?: default - } - - override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { - val name = key ?: property.name.asName() - obj.config[name] = 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, name: Name? = null, inherited: Boolean = false) = - VisualObjectDelegateWrapper(this, name, default, inherited) { it.string } - -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, name: Name? = null, inherited: Boolean = false) = - VisualObjectDelegateWrapper(this, name, default, inherited) { it.number } - -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, name: Name? = null, inherited: Boolean = false) = - VisualObjectDelegateWrapper(this, name, default, inherited) { it.int } - - -fun VisualObject.node(name: Name? = null, inherited: Boolean = true) = - VisualObjectDelegateWrapper(this, name, null, inherited) { it.node } - -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, name: Name? = null, inherited: Boolean = false) = - VisualObjectDelegateWrapper(this, name, default, inherited) { it.string } - -@JvmName("safeBoolean") -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, name: Name? = null, inherited: Boolean = false) = - VisualObjectDelegateWrapper(this, name, default, inherited) { it.number } - -@JvmName("safeDouble") -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, name: Name? = null, inherited: Boolean = false) = - VisualObjectDelegateWrapper(this, name, default, inherited) { it.int } - - -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( - name: Name? = null, - transformer: (Sequence>) -> T -): ReadOnlyProperty { - return object : ReadOnlyProperty { - override fun getValue(thisRef: Any?, property: KProperty<*>): T { - val actualName = name ?: property.name.asName() - val sequence = sequence> { - var thisObj: VisualObject? = this@merge - while (thisObj != null) { - thisObj.config[actualName]?.let { yield(it) } - thisObj = thisObj.parent - } - } - return transformer(sequence) - } - } -} \ No newline at end of file diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/valueWidget.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/valueWidget.kt deleted file mode 100644 index 80e44a35..00000000 --- a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/valueWidget.kt +++ /dev/null @@ -1,23 +0,0 @@ -package hep.dataforge.vis - -import hep.dataforge.meta.* -import hep.dataforge.meta.descriptors.ValueDescriptor -import hep.dataforge.values.asValue - -/** - * Extension property to access the "widget" key of [ValueDescriptor] - */ -var ValueDescriptor.widget: Meta - get() = getProperty("widget").node ?: Meta.EMPTY - set(value) { - setProperty("widget", value) - } - -/** - * Extension property to access the "widget.type" key of [ValueDescriptor] - */ -var ValueDescriptor.widgetType: String? - get() = getProperty("widget.type").string - set(value) { - setProperty("widget.type", value?.asValue()) - } \ No newline at end of file diff --git a/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/js/react.kt b/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/js/react.kt deleted file mode 100644 index 2611e7db..00000000 --- a/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/js/react.kt +++ /dev/null @@ -1,17 +0,0 @@ -package hep.dataforge.js - -import react.RComponent -import kotlin.properties.ReadWriteProperty -import kotlin.reflect.KProperty - -fun RComponent<*, *>.initState(init: () -> T): ReadWriteProperty, T> = - object : ReadWriteProperty, T> { - val pair = react.useState(init) - override fun getValue(thisRef: RComponent<*, *>, property: KProperty<*>): T { - return pair.first - } - - override fun setValue(thisRef: RComponent<*, *>, property: KProperty<*>, value: T) { - pair.second(value) - } - } \ No newline at end of file diff --git a/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/editor/ConfigEditorComponent.kt b/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/editor/ConfigEditorComponent.kt deleted file mode 100644 index a6f3657f..00000000 --- a/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/editor/ConfigEditorComponent.kt +++ /dev/null @@ -1,209 +0,0 @@ -package hep.dataforge.vis.editor - -import hep.dataforge.meta.* -import hep.dataforge.meta.descriptors.ItemDescriptor -import hep.dataforge.meta.descriptors.NodeDescriptor -import hep.dataforge.meta.descriptors.defaultItem -import hep.dataforge.meta.descriptors.get -import hep.dataforge.names.Name -import hep.dataforge.names.NameToken -import hep.dataforge.names.plus -import hep.dataforge.values.asValue -import kotlinx.html.InputType -import kotlinx.html.classes -import kotlinx.html.js.onChangeFunction -import kotlinx.html.js.onClickFunction -import org.w3c.dom.Element -import org.w3c.dom.HTMLInputElement -import org.w3c.dom.events.Event -import react.RBuilder -import react.RComponent -import react.RProps -import react.dom.* -import react.setState - -interface ConfigEditorProps : RProps { - /** - * Root config object - always non null - */ - var root: Config - - /** - * Full path to the displayed node in [root]. Could be empty - */ - var name: Name - - /** - * Root default - */ - var default: Meta? - - /** - * Root descriptor - */ - var descriptor: NodeDescriptor? - - var listen: Boolean -} - -class ConfigEditorComponent : RComponent() { - - override fun TreeState.init() { - expanded = true - } - - override fun componentDidMount() { - if (props.listen) { - props.root.onChange(this) { name, _, _ -> - if (name == props.name) { - forceUpdate() - } - } - } - } - - override fun componentWillUnmount() { - props.root.removeListener(this) - } - - private val onClick: (Event) -> Unit = { - setState { - expanded = !expanded - } - } - - private val onValueChange: (Event) -> Unit = { - val value = (it.target as HTMLInputElement).value - try { - if(value.isEmpty()){ - props.root.remove(props.name) - } - props.root.setValue(props.name, value.asValue()) - } catch (ex: Exception) { - console.error("Can't set config property ${props.name} to $value") - } - } - - - override fun RBuilder.render() { - val item = props.root[props.name] - val descriptorItem: ItemDescriptor? = props.descriptor?.get(props.name) - val defaultItem = props.default?.get(props.name) - val actualItem = item ?: defaultItem ?: descriptorItem?.defaultItem() - val token = props.name.last()?.toString() ?: "Properties" - - when (actualItem) { - is MetaItem.NodeItem -> { - div("d-inline-block text-truncate") { - span("tree-caret") { - attrs { - if (state.expanded) { - classes += "tree-caret-down" - } - onClickFunction = onClick - } - } - span("tree-label") { - +token - attrs { - if (item == null) { - classes += "tree-label-inactive" - } - } - } - } - if (state.expanded) { - ul("tree") { - val keys = buildSet { - item?.node?.items?.keys?.let { addAll(it) } - defaultItem?.node?.items?.keys?.let { addAll(it) } - (descriptorItem as? NodeDescriptor)?.items?.keys?.forEach { - add(NameToken(it)) - } - } - - keys.forEach { token -> - li("tree-item") { - child(ConfigEditorComponent::class) { - attrs { - root = props.root - name = props.name + token - this.default = props.default - this.descriptor = props.descriptor - listen = false - } - } - } - } - } - } - } - is MetaItem.ValueItem -> { - div("d-inline-block text-truncate") { - div("row") { - div("col") { - p("tree-label") { - +token - attrs { - if (item == null) { - classes += "tree-label-inactive" - } - } - } - } - div("col") { - div("float-right") { - input(type = InputType.text) { - attrs { - defaultValue = actualItem.value.string - onChangeFunction = onValueChange - } - } - //+actualItem.value.toString() - } - } - } - } - } - } - } - -} - -fun Element.configEditor(config: Config, descriptor: NodeDescriptor? = null, default: Meta? = null) { - render(this) { - child(ConfigEditorComponent::class) { - attrs { - root = config - name = Name.EMPTY - this.descriptor = descriptor - this.default = default - listen = true - } - } - } -} - -fun RBuilder.configEditor(config: Config, descriptor: NodeDescriptor? = null, default: Meta? = null) { - child(ConfigEditorComponent::class) { - attrs { - root = config - name = Name.EMPTY - this.descriptor = descriptor - this.default = default - listen = true - } - } -} - -fun RBuilder.configEditor(obj: Configurable, descriptor: NodeDescriptor? = obj.descriptor, default: Meta? = null) { - child(ConfigEditorComponent::class) { - attrs { - root = obj.config - name = Name.EMPTY - this.descriptor = descriptor - this.default = default - listen = true - } - } -} diff --git a/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/editor/ObjectTreeComponent.kt b/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/editor/ObjectTreeComponent.kt deleted file mode 100644 index cca127a2..00000000 --- a/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/editor/ObjectTreeComponent.kt +++ /dev/null @@ -1,111 +0,0 @@ -package hep.dataforge.vis.editor - -import hep.dataforge.names.Name -import hep.dataforge.names.plus -import hep.dataforge.vis.VisualGroup -import hep.dataforge.vis.VisualObject -import hep.dataforge.vis.isEmpty -import kotlinx.html.classes -import kotlinx.html.js.onClickFunction -import org.w3c.dom.Element -import org.w3c.dom.events.Event -import react.* -import react.dom.* - -interface ObjectTreeProps : RProps { - var name: Name - var obj: VisualObject - var clickCallback: (Name) -> Unit -} - -interface TreeState : RState { - var expanded: Boolean -} - -class ObjectTreeComponent : RComponent() { - - override fun TreeState.init() { - expanded = false - } - - private val onClick: (Event) -> Unit = { - setState { - expanded = !expanded - } - } - - override fun RBuilder.render() { - val token = props.name.last()?.toString() ?: "World" - val obj = props.obj - - //display as node if any child is visible - if (obj is VisualGroup && obj.children.keys.any { !it.body.startsWith("@") }) { - div("d-inline-block text-truncate") { - span("tree-caret") { - attrs { - if (state.expanded) { - classes += "tree-caret-down" - } - onClickFunction = onClick - } - } - a("#",classes = "tree-label") { - +token - attrs { - onClickFunction = { props.clickCallback(props.name) } - } - } - } - if (state.expanded) { - ul("tree") { - obj.children.entries - .filter { !it.key.toString().startsWith("@") } // ignore statics and other hidden children - .sortedBy { (it.value as? VisualGroup)?.isEmpty ?: true } - .forEach { (childToken, child) -> - li("tree-item") { - child(ObjectTreeComponent::class) { - attrs { - name = props.name + childToken - this.obj = child - clickCallback = props.clickCallback - } - } - } - } - } - } - } else { - div("d-inline-block text-truncate") { - span("tree-leaf") {} - a("#",classes = "tree-label") { - +token - attrs { - onClickFunction = { props.clickCallback(props.name) } - } - } - } - } - } -} - -fun RBuilder.objectTree( - obj: VisualObject, - clickCallback: (Name) -> Unit = {} -) = card("Object tree") { - child(ObjectTreeComponent::class) { - attrs { - name = Name.EMPTY - this.obj = obj - this.clickCallback = clickCallback - } - } -} - -fun Element.objectTree( - obj: VisualObject, - clickCallback: (Name) -> Unit = {} -) { - render(this) { - objectTree(obj, clickCallback) - } -} \ No newline at end of file diff --git a/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/editor/bootstrap.kt b/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/editor/bootstrap.kt deleted file mode 100644 index 02149034..00000000 --- a/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/editor/bootstrap.kt +++ /dev/null @@ -1,73 +0,0 @@ -package hep.dataforge.vis.editor - -import kotlinx.html.* -import kotlinx.html.js.div -import org.w3c.dom.HTMLElement -import react.RBuilder -import react.ReactElement -import react.dom.div -import react.dom.h3 - -inline fun TagConsumer.card(title: String, crossinline block: TagConsumer.() -> Unit) { - div("card w-100") { - div("card-body") { - h3(classes = "card-title") { +title } - block() - } - } -} - -inline fun RBuilder.card(title: String, crossinline block: RBuilder.() -> Unit): ReactElement = div("card w-100") { - div("card-body") { - h3(classes = "card-title") { +title } - 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/editor/jsTree.kt b/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/editor/jsTree.kt deleted file mode 100644 index a9c876ae..00000000 --- a/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/editor/jsTree.kt +++ /dev/null @@ -1,77 +0,0 @@ -package hep.dataforge.vis.editor - -import hep.dataforge.names.Name -import hep.dataforge.names.plus -import hep.dataforge.vis.VisualGroup -import hep.dataforge.vis.VisualObject -import hep.dataforge.vis.isEmpty -import kotlinx.html.TagConsumer -import kotlinx.html.dom.append -import kotlinx.html.js.* -import org.w3c.dom.Element -import org.w3c.dom.HTMLElement -import org.w3c.dom.HTMLSpanElement -import kotlin.dom.clear - -//fun Element.displayObjectTree( -// obj: VisualObject, -// clickCallback: (Name) -> Unit = {} -//) { -// clear() -// append { -// card("Object tree") { -// subTree(Name.EMPTY, obj, clickCallback) -// } -// } -//} -// -private fun TagConsumer.subTree( - name: Name, - obj: VisualObject, - clickCallback: (Name) -> Unit -) { - 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("@") }) { - lateinit var toggle: HTMLSpanElement - div("d-inline-block text-truncate") { - toggle = span("objTree-caret") - label("objTree-label") { - +token - onClickFunction = { clickCallback(name) } - } - } - val subtree = ul("objTree-subtree") - toggle.onclick = { - toggle.classList.toggle("objTree-caret-down") - subtree.apply { - //If expanded, add children dynamically - if (toggle.classList.contains("objTree-caret-down")) { - obj.children.entries - .filter { !it.key.toString().startsWith("@") } // ignore statics and other hidden children - .sortedBy { (it.value as? VisualGroup)?.isEmpty ?: true } - .forEach { (childToken, child) -> - append { - li().apply { - subTree(name + childToken, child, clickCallback) - } - } - } - } else { - // if not, clear them to conserve memory on very long lists - this.clear() - } - } - } - } else { - div("d-inline-block text-truncate") { - span("objTree-leaf") - label("objTree-label") { - +token - onClickFunction = { clickCallback(name) } - } - } - } -} - diff --git a/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/editor/jsoneditor.kt b/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/editor/jsoneditor.kt deleted file mode 100644 index 800396cb..00000000 --- a/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/editor/jsoneditor.kt +++ /dev/null @@ -1,185 +0,0 @@ -@file:Suppress( - "INTERFACE_WITH_SUPERCLASS", - "OVERRIDING_FINAL_MEMBER", - "RETURN_TYPE_MISMATCH_ON_OVERRIDE", - "CONFLICTING_OVERLOADS", - "EXTERNAL_DELEGATION" -) - -package hep.dataforge.vis.editor - -import org.w3c.dom.HTMLElement - -external interface Node { - var field: String - var value: String? get() = definedExternally; set(value) = definedExternally - var path: dynamic -} - -external interface NodeName { - var path: Array - var type: dynamic /* 'object' | 'array' */ - var size: Number -} - -external interface ValidationError { - var path: dynamic - var message: String -} - -external interface Template { - var text: String - var title: String - var className: String? get() = definedExternally; set(value) = definedExternally - var field: String - var value: Any -} - -external interface `T$6` { - var startFrom: Number - var options: Array -} - -external interface AutoCompleteOptions { - var confirmKeys: Array? get() = definedExternally; set(value) = definedExternally - var caseSensitive: Boolean? get() = definedExternally; set(value) = definedExternally -// var getOptions: AutoCompleteOptionsGetter? get() = definedExternally; set(value) = definedExternally -} - -external interface SelectionPosition { - var row: Number - var column: Number -} - -external interface SerializableNode { - var value: Any - var path: dynamic -} - -external interface Color { - var rgba: Array - var hsla: Array - var rgbString: String - var rgbaString: String - var hslString: String - var hslaString: String - var hex: String -} - -//external interface `T$0` { -// var field: Boolean -// var value: Boolean -//} -// -//external interface `T$1` { -// @nativeGetter -// operator fun get(key: String): String? -// -// @nativeSetter -// operator fun set(key: String, value: String) -//} - -//external interface Languages { -// @nativeGetter -// operator fun get(lang: String): `T$1`? -// -// @nativeSetter -// operator fun set(lang: String, value: `T$1`) -//} - -external interface JSONEditorOptions { -// var ace: AceAjax.Ace? get() = definedExternally; set(value) = definedExternally -// var ajv: Ajv? get() = definedExternally; set(value) = definedExternally - var onChange: (() -> Unit)? get() = definedExternally; set(value) = definedExternally - var onChangeJSON: ((json: Any) -> Unit)? get() = definedExternally; set(value) = definedExternally - var onChangeText: ((jsonString: String) -> Unit)? get() = definedExternally; set(value) = definedExternally - var onEditable: ((node: Node) -> dynamic)? get() = definedExternally; set(value) = definedExternally - var onError: ((error: Error) -> Unit)? get() = definedExternally; set(value) = definedExternally - var onModeChange: ((newMode: dynamic /* 'tree' | 'view' | 'form' | 'code' | 'text' */, oldMode: dynamic /* 'tree' | 'view' | 'form' | 'code' | 'text' */) -> Unit)? get() = definedExternally; set(value) = definedExternally - var onNodeName: ((nodeName: NodeName) -> String?)? get() = definedExternally; set(value) = definedExternally - var onValidate: ((json: Any) -> dynamic)? get() = definedExternally; set(value) = definedExternally - var escapeUnicode: Boolean? get() = definedExternally; set(value) = definedExternally - var sortObjectKeys: Boolean? get() = definedExternally; set(value) = definedExternally - var history: Boolean? get() = definedExternally; set(value) = definedExternally - var mode: dynamic /* 'tree' | 'view' | 'form' | 'code' | 'text' */ - var modes: Array? get() = definedExternally; set(value) = definedExternally - var name: String? get() = definedExternally; set(value) = definedExternally - var schema: Any? get() = definedExternally; set(value) = definedExternally - var schemaRefs: Any? get() = definedExternally; set(value) = definedExternally - var search: Boolean? get() = definedExternally; set(value) = definedExternally - var indentation: Number? get() = definedExternally; set(value) = definedExternally - var theme: String? get() = definedExternally; set(value) = definedExternally - var templates: Array