0
CHANGELOG.md
Normal file
83
README.md
@ -2,19 +2,19 @@
|
|||||||
|
|
||||||
# DataForge Visualization Platform
|
# DataForge Visualization Platform
|
||||||
|
|
||||||
## Table of contents
|
## Table of Contents
|
||||||
|
|
||||||
* [Introduction](#introduction)
|
* [Introduction](#introduction)
|
||||||
* [Features](#features)
|
* [Features](#features)
|
||||||
* [About DataForge](#about-dataforge)
|
* [About DataForge](#about-dataforge)
|
||||||
* [Modules contained in this repository](#modules-contained-in-this-repository)
|
* [Modules contained in this repository](#modules-contained-in-this-repository)
|
||||||
* [dataforge-vis-common](#dataforge-vis-common)
|
* [visionforge-core](#visionforge-core)
|
||||||
* [dataforge-vis-spatial](#dataforge-vis-spatial)
|
* [visionforge-solid](#visionforge-solid)
|
||||||
* [dataforge-vis-spatial-gdml](#dataforge-vis-spatial-gdml)
|
* [visionforge-gdml](#visionforge-gdml)
|
||||||
* [dataforge-vis-jsroot](#dataforge-vis-jsroot)
|
* [Visualization for External Systems](#visualization-for-external-systems)
|
||||||
* [Demonstrations](#demonstrations)
|
* [Demonstrations](#demonstrations)
|
||||||
* [Spatial Showcase](#spatial-showcase)
|
* [Simple Example - Spatial Showcase](#simple-example---spatial-showcase)
|
||||||
* [Muon Monitor](#muon-monitor-visualization)
|
* [Full-Stack Application Example - Muon Monitor](#full-stack-application-example---muon-monitor-visualization)
|
||||||
* [GDML Example](#gdml-example)
|
* [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.
|
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.
|
Other applications including 2D plots are planned for the future.
|
||||||
|
|
||||||
The project is being developed as a Kotlin multiplatform application, currently targeting browser
|
The project is developed as a [Kotlin multiplatform](https://kotlinlang.org/docs/reference/multiplatform.html)
|
||||||
JavaScript and JVM.
|
application, currently targeting browser JavaScript and JVM.
|
||||||
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
@ -41,7 +41,6 @@ The main framework's features for now include:
|
|||||||
- Settings export and import
|
- Settings export and import
|
||||||
- Multiple platform support
|
- Multiple platform support
|
||||||
|
|
||||||
|
|
||||||
## About DataForge
|
## About DataForge
|
||||||
|
|
||||||
DataForge is a software framework for automated scientific data processing. DataForge Visualization
|
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
|
## Modules contained in this repository
|
||||||
|
|
||||||
### dataforge-vis-common
|
### visionforge-core
|
||||||
|
|
||||||
Contains a general hierarchy of classes and interfaces useful for visualization.
|
Contains a general hierarchy of classes and interfaces useful for visualization.
|
||||||
This module is not specific to 3D-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
|
##### 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
|
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.
|
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
|
##### Styles
|
||||||
|
|
||||||
`VisualGroup3D` has a `styleSheet` property that can optionally define styles at the Group's
|
`SolidGroup` has a `styleSheet` property that can optionally define styles at the Group's
|
||||||
level. Styles are applied to child (descendant) objects using `styles: List<String>` property defined
|
level. Styles are applied to child (descendant) objects using `Vision.styles: List<String>` property.
|
||||||
in `VisualObject`.
|
|
||||||
|
|
||||||
|
|
||||||
### dataforge-vis-spatial-gdml
|
### visionforge-gdml
|
||||||
|
|
||||||
GDML bindings for 3D visualization (to be moved to gdml project).
|
GDML bindings for 3D visualization (to be moved to gdml project).
|
||||||
|
|
||||||
|
|
||||||
### dataforge-vis-jsroot
|
## Visualization for External Systems
|
||||||
|
|
||||||
Some JSROOT bindings.
|
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
|
||||||
Note: Currently, this part is experimental and put here for completeness. This module may not build.
|
of the external system to that of `visionforge`. Performing such integration is a work
|
||||||
|
currently in progress.
|
||||||
|
|
||||||
|
|
||||||
## Demonstrations
|
## 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.
|
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.
|
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
|
[More details](demo/spatial-showcase/README.md)
|
||||||
`build/distribuions/spatial-showcase-js-0.1.0-dev/index.html` file in your browser.
|
|
||||||
|
|
||||||
##### Example view:
|
**Example view:**
|
||||||
|
|
||||||
![](doc/resources/spatial-showcase.png)
|
![](doc/resources/spatial-showcase.png)
|
||||||
|
|
||||||
### Muon Monitor Visualization
|
|
||||||
|
### Full-Stack Application Example - Muon Monitor Visualization
|
||||||
|
|
||||||
A full-stack application example, showing the
|
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
|
**Example view:**
|
||||||
`demo/muon-monitor/application/run` task.
|
|
||||||
|
|
||||||
##### Example view:
|
|
||||||
|
|
||||||
![](doc/resources/muon-monitor.png)
|
![](doc/resources/muon-monitor.png)
|
||||||
|
|
||||||
|
|
||||||
### GDML Example
|
### GDML Example
|
||||||
|
|
||||||
Visualization example for geometry defined as GDML file.
|
Visualization example for geometry defined as GDML file.
|
||||||
|
|
||||||
To build the app, run `demo/gdml/Tasks/distribution/installJsDist` task, then open
|
[More details](demo/gdml/README.md)
|
||||||
`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:
|
##### Example view:
|
||||||
|
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
import scientifik.fx
|
import scientifik.useFx
|
||||||
import scientifik.serialization
|
import scientifik.useSerialization
|
||||||
|
|
||||||
val dataforgeVersion by extra("0.1.7")
|
val dataforgeVersion by extra("0.1.8")
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
val toolsVersion = "0.4.2"
|
id("scientifik.mpp") apply false
|
||||||
id("scientifik.mpp") version toolsVersion apply false
|
id("scientifik.jvm") apply false
|
||||||
id("scientifik.jvm") version toolsVersion apply false
|
id("scientifik.js") apply false
|
||||||
id("scientifik.js") version toolsVersion apply false
|
id("scientifik.publish") apply false
|
||||||
id("scientifik.publish") version toolsVersion apply false
|
id("org.jetbrains.changelog") version "0.4.0"
|
||||||
id("org.openjfx.javafxplugin") version "0.0.8" apply false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
@ -17,24 +16,20 @@ allprojects {
|
|||||||
mavenLocal()
|
mavenLocal()
|
||||||
maven("https://dl.bintray.com/pdvrieze/maven")
|
maven("https://dl.bintray.com/pdvrieze/maven")
|
||||||
maven("http://maven.jzy3d.org/releases")
|
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"
|
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 bintrayRepo by extra("dataforge")
|
||||||
val fxVersion by extra("14")
|
val fxVersion by extra("14")
|
||||||
|
|
||||||
subprojects {
|
subprojects {
|
||||||
|
if(name.startsWith("visionforge")) {
|
||||||
apply(plugin = "scientifik.publish")
|
apply(plugin = "scientifik.publish")
|
||||||
serialization()
|
|
||||||
afterEvaluate {
|
|
||||||
fx(scientifik.FXModule.CONTROLS, version = fxVersion)
|
|
||||||
}
|
}
|
||||||
|
useSerialization()
|
||||||
|
useFx(scientifik.FXModule.CONTROLS, version = fxVersion)
|
||||||
}
|
}
|
@ -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"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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<String>
|
|
||||||
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<String>) {
|
|
||||||
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<PropertyListener>()
|
|
||||||
|
|
||||||
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)
|
|
||||||
//}
|
|
@ -1,39 +0,0 @@
|
|||||||
package hep.dataforge.vis
|
|
||||||
|
|
||||||
import hep.dataforge.context.*
|
|
||||||
import hep.dataforge.meta.Meta
|
|
||||||
import kotlin.reflect.KClass
|
|
||||||
|
|
||||||
interface VisualFactory<T : VisualObject> {
|
|
||||||
val type: KClass<T>
|
|
||||||
operator fun invoke(
|
|
||||||
context: Context,
|
|
||||||
parent: VisualObject?,
|
|
||||||
meta: Meta
|
|
||||||
): T
|
|
||||||
}
|
|
||||||
|
|
||||||
class 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<VisualFactory<*>>(VISUAL_FACTORY_TYPE).mapKeys { it.value.type }
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fun <reified T : VisualObject> buildVisual(parent: VisualObject?, meta: Meta): T? {
|
|
||||||
return visualFactories[T::class]?.invoke(context, parent, meta) as T?
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object : PluginFactory<Visual> {
|
|
||||||
override val tag: PluginTag = PluginTag(name = "visual", group = PluginTag.DATAFORGE_GROUP)
|
|
||||||
override val type: KClass<out Visual> = Visual::class
|
|
||||||
|
|
||||||
override fun invoke(meta: Meta, context: Context): Visual =
|
|
||||||
Visual(meta)
|
|
||||||
|
|
||||||
const val VISUAL_FACTORY_TYPE = "visual.factory"
|
|
||||||
}
|
|
||||||
}
|
|
@ -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<String>
|
|
||||||
|
|
||||||
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
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
@ -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<VisualObject, MetaItem<*>?> {
|
|
||||||
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<T>(
|
|
||||||
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<Any?, T> {
|
|
||||||
|
|
||||||
//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 <T : Configurable> Configurable.spec(spec: Specification<T>, key: String? = null) = ChildConfigDelegate<T>(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 <reified E : Enum<E>> VisualObject.enum(default: E, name: Name? = null, inherited: Boolean = false) =
|
|
||||||
VisualObjectDelegateWrapper(this, name, default, inherited) { item ->
|
|
||||||
item.string?.let { enumValueOf<E>(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
//merge properties
|
|
||||||
|
|
||||||
fun <T> VisualObject.merge(
|
|
||||||
name: Name? = null,
|
|
||||||
transformer: (Sequence<MetaItem<*>>) -> T
|
|
||||||
): ReadOnlyProperty<VisualObject, T> {
|
|
||||||
return object : ReadOnlyProperty<Any?, T> {
|
|
||||||
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
|
|
||||||
val actualName = name ?: property.name.asName()
|
|
||||||
val sequence = sequence<MetaItem<*>> {
|
|
||||||
var thisObj: VisualObject? = this@merge
|
|
||||||
while (thisObj != null) {
|
|
||||||
thisObj.config[actualName]?.let { yield(it) }
|
|
||||||
thisObj = thisObj.parent
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return transformer(sequence)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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())
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
package hep.dataforge.js
|
|
||||||
|
|
||||||
import react.RComponent
|
|
||||||
import kotlin.properties.ReadWriteProperty
|
|
||||||
import kotlin.reflect.KProperty
|
|
||||||
|
|
||||||
fun <T> RComponent<*, *>.initState(init: () -> T): ReadWriteProperty<RComponent<*, *>, T> =
|
|
||||||
object : ReadWriteProperty<RComponent<*, *>, 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)
|
|
||||||
}
|
|
||||||
}
|
|
@ -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<ConfigEditorProps, TreeState>() {
|
|
||||||
|
|
||||||
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<NameToken> {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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<ObjectTreeProps, TreeState>() {
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
@ -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<HTMLElement>.card(title: String, crossinline block: TagConsumer<HTMLElement>.() -> 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<HTMLElement>.accordion(id: String, elements: Map<String, DIV.() -> 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<String, DIV.() -> Unit>()
|
|
||||||
fun entry(title: String, block: DIV.() -> Unit) {
|
|
||||||
map[title] = block
|
|
||||||
}
|
|
||||||
|
|
||||||
fun build(consumer: TagConsumer<HTMLElement>, id: String) {
|
|
||||||
consumer.accordion(id, map)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun TagConsumer<HTMLElement>.accordion(id: String, block: AccordionBuilder.() -> Unit) {
|
|
||||||
AccordionBuilder().apply(block).build(this, id)
|
|
||||||
}
|
|
@ -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<HTMLElement>.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) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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<String>
|
|
||||||
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<String>
|
|
||||||
}
|
|
||||||
|
|
||||||
external interface AutoCompleteOptions {
|
|
||||||
var confirmKeys: Array<Number>? 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<Number>
|
|
||||||
var hsla: Array<Number>
|
|
||||||
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<dynamic /* 'tree' | 'view' | 'form' | 'code' | 'text' */>? 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<Template>? get() = definedExternally; set(value) = definedExternally
|
|
||||||
var autocomplete: AutoCompleteOptions? get() = definedExternally; set(value) = definedExternally
|
|
||||||
var mainMenuBar: Boolean? get() = definedExternally; set(value) = definedExternally
|
|
||||||
var navigationBar: Boolean? get() = definedExternally; set(value) = definedExternally
|
|
||||||
var statusBar: Boolean? get() = definedExternally; set(value) = definedExternally
|
|
||||||
var onTextSelectionChange: ((start: SelectionPosition, end: SelectionPosition, text: String) -> Unit)? get() = definedExternally; set(value) = definedExternally
|
|
||||||
var onSelectionChange: ((start: SerializableNode, end: SerializableNode) -> Unit)? get() = definedExternally; set(value) = definedExternally
|
|
||||||
var onEvent: ((node: Node, event: String) -> Unit)? get() = definedExternally; set(value) = definedExternally
|
|
||||||
var colorPicker: Boolean? get() = definedExternally; set(value) = definedExternally
|
|
||||||
var onColorPicker: ((parent: HTMLElement, color: String, onChange: (color: Color) -> Unit) -> Unit)? get() = definedExternally; set(value) = definedExternally
|
|
||||||
var timestampTag: Boolean? get() = definedExternally; set(value) = definedExternally
|
|
||||||
var language: String? get() = definedExternally; set(value) = definedExternally
|
|
||||||
//var languages: Languages? get() = definedExternally; set(value) = definedExternally
|
|
||||||
var modalAnchor: HTMLElement? get() = definedExternally; set(value) = definedExternally
|
|
||||||
var enableSort: Boolean? get() = definedExternally; set(value) = definedExternally
|
|
||||||
var enableTransform: Boolean? get() = definedExternally; set(value) = definedExternally
|
|
||||||
var maxVisibleChilds: Number? get() = definedExternally; set(value) = definedExternally
|
|
||||||
}
|
|
||||||
|
|
||||||
external interface JsonPath {
|
|
||||||
var path: dynamic
|
|
||||||
}
|
|
||||||
|
|
||||||
external interface EditorSelection {
|
|
||||||
var start: SerializableNode
|
|
||||||
var end: SerializableNode
|
|
||||||
}
|
|
||||||
|
|
||||||
external interface TextSelection {
|
|
||||||
var start: SelectionPosition
|
|
||||||
var end: SelectionPosition
|
|
||||||
var text: String
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsModule("jsoneditor")
|
|
||||||
@JsNonModule
|
|
||||||
external open class JSONEditor(
|
|
||||||
container: HTMLElement,
|
|
||||||
options: JSONEditorOptions? = definedExternally /* null */,
|
|
||||||
json: dynamic = definedExternally /* null */
|
|
||||||
) {
|
|
||||||
open fun collapseAll()
|
|
||||||
open fun destroy()
|
|
||||||
open fun expandAll()
|
|
||||||
open fun focus()
|
|
||||||
open fun get(): Any
|
|
||||||
open fun getMode(): dynamic /* 'tree' | 'view' | 'form' | 'code' | 'text' */
|
|
||||||
open fun getName(): String?
|
|
||||||
open fun getNodesByRange(start: JsonPath, end: JsonPath): Array<SerializableNode>
|
|
||||||
open fun getSelection(): EditorSelection
|
|
||||||
open fun getText(): String
|
|
||||||
open fun getTextSelection(): TextSelection
|
|
||||||
open fun refresh()
|
|
||||||
open fun set(json: Any)
|
|
||||||
open fun setMode(mode: String /* 'tree' */)
|
|
||||||
open fun setMode(mode: String /* 'view' */)
|
|
||||||
open fun setMode(mode: String /* 'form' */)
|
|
||||||
open fun setMode(mode: String /* 'code' */)
|
|
||||||
open fun setMode(mode: String /* 'text' */)
|
|
||||||
open fun setName(name: String? = definedExternally /* null */)
|
|
||||||
open fun setSchema(schema: Any?, schemaRefs: Any? = definedExternally /* null */)
|
|
||||||
open fun setSelection(start: JsonPath, end: JsonPath)
|
|
||||||
open fun setText(jsonString: String)
|
|
||||||
open fun setTextSelection(start: SelectionPosition, end: SelectionPosition)
|
|
||||||
open fun update(json: Any)
|
|
||||||
open fun updateText(jsonString: String)
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
var VALID_OPTIONS: Array<String>
|
|
||||||
// var ace: AceAjax.Ace
|
|
||||||
// var Ajv: Ajv
|
|
||||||
var VanillaPicker: Any
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,107 +0,0 @@
|
|||||||
package hep.dataforge.vis.editor
|
|
||||||
|
|
||||||
import hep.dataforge.meta.Meta
|
|
||||||
import hep.dataforge.meta.MetaBuilder
|
|
||||||
import hep.dataforge.meta.descriptors.NodeDescriptor
|
|
||||||
import hep.dataforge.names.Name
|
|
||||||
import hep.dataforge.names.isEmpty
|
|
||||||
import hep.dataforge.vis.VisualObject
|
|
||||||
import org.w3c.dom.Element
|
|
||||||
import react.RBuilder
|
|
||||||
import react.ReactElement
|
|
||||||
import react.dom.li
|
|
||||||
import react.dom.nav
|
|
||||||
import react.dom.ol
|
|
||||||
import react.dom.render
|
|
||||||
import kotlin.collections.set
|
|
||||||
|
|
||||||
////FIXME something rotten in JS-Meta converter
|
|
||||||
//fun Meta.toDynamic() = JSON.parse<dynamic>(toJson().toString())
|
|
||||||
//
|
|
||||||
////TODO add node descriptor instead of configuring property selector
|
|
||||||
//fun Element.displayPropertyEditor(
|
|
||||||
// name: Name,
|
|
||||||
// item: VisualObject,
|
|
||||||
// propertySelector: (VisualObject) -> Meta = { it.config }
|
|
||||||
//) {
|
|
||||||
// clear()
|
|
||||||
//
|
|
||||||
// append {
|
|
||||||
// card("Properties") {
|
|
||||||
// if (!name.isEmpty()) {
|
|
||||||
// nav {
|
|
||||||
// attributes["aria-label"] = "breadcrumb"
|
|
||||||
// ol("breadcrumb") {
|
|
||||||
// name.tokens.forEach { token ->
|
|
||||||
// li("breadcrumb-item") {
|
|
||||||
// +token.toString()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// val dMeta: dynamic = propertySelector(item).toDynamic()
|
|
||||||
// val options: JSONEditorOptions = jsObject {
|
|
||||||
// mode = "form"
|
|
||||||
// onChangeJSON = { item.config.update(DynamicMeta(it.asDynamic())) }
|
|
||||||
// }
|
|
||||||
// JSONEditor(div(), options, dMeta)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// val styles = item.styles
|
|
||||||
// if (styles.isNotEmpty()) {
|
|
||||||
// card("Styles") {
|
|
||||||
// item.styles.forEach { style ->
|
|
||||||
// val styleMeta = item.findStyle(style)
|
|
||||||
// h4("container") { +style }
|
|
||||||
// if (styleMeta != null) {
|
|
||||||
// div("container").apply {
|
|
||||||
// val options: JSONEditorOptions = jsObject {
|
|
||||||
// mode = "view"
|
|
||||||
// }
|
|
||||||
// JSONEditor(
|
|
||||||
// this,
|
|
||||||
// options,
|
|
||||||
// styleMeta.toDynamic()
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
fun RBuilder.visualPropertyEditor(
|
|
||||||
path: Name,
|
|
||||||
item: VisualObject,
|
|
||||||
descriptor: NodeDescriptor? = item.descriptor,
|
|
||||||
title: String = "Properties",
|
|
||||||
default: MetaBuilder.() -> Unit = {}
|
|
||||||
): ReactElement = card(title) {
|
|
||||||
if (!path.isEmpty()) {
|
|
||||||
nav {
|
|
||||||
attrs {
|
|
||||||
attributes["aria-label"] = "breadcrumb"
|
|
||||||
}
|
|
||||||
ol("breadcrumb") {
|
|
||||||
path.tokens.forEach { token ->
|
|
||||||
li("breadcrumb-item") {
|
|
||||||
+token.toString()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
configEditor(item, descriptor, Meta(default))
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Element.visualPropertyEditor(
|
|
||||||
path: Name,
|
|
||||||
item: VisualObject,
|
|
||||||
descriptor: NodeDescriptor? = item.descriptor,
|
|
||||||
title: String = "Properties",
|
|
||||||
default: MetaBuilder.() -> Unit = {}
|
|
||||||
) = render(this) {
|
|
||||||
visualPropertyEditor(path, item, descriptor, title, default)
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
/* Remove default bullets */
|
|
||||||
ul, .tree {
|
|
||||||
list-style-type: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Style the caret/arrow */
|
|
||||||
.tree-caret {
|
|
||||||
cursor: pointer;
|
|
||||||
user-select: none; /* Prevent text selection */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create the caret/arrow with a unicode, and style it */
|
|
||||||
.tree-caret::before {
|
|
||||||
content: "\25B6";
|
|
||||||
color: black;
|
|
||||||
display: inline-block;
|
|
||||||
margin-right: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tree-leaf{
|
|
||||||
user-select: none;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tree-leaf::before {
|
|
||||||
content: "\25C6";
|
|
||||||
color: black;
|
|
||||||
display: inline-block;
|
|
||||||
margin-right: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Rotate the caret/arrow icon when clicked on (using JavaScript) */
|
|
||||||
.tree-caret-down::before {
|
|
||||||
transform: rotate(90deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.tree-label-inactive {
|
|
||||||
background-color: lightgrey;
|
|
||||||
color: gray;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
@ -12,7 +12,7 @@ plugins {
|
|||||||
val kotlinVersion: String by rootProject.extra
|
val kotlinVersion: String by rootProject.extra
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":dataforge-vis-spatial-js"))
|
implementation(project(":visionforge-spatial-js"))
|
||||||
testCompile(kotlin("test-js"))
|
testCompile(kotlin("test-js"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ fun main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun start(state: dynamic): ApplicationBase? {
|
fun start(state: dynamic): ApplicationBase? {
|
||||||
return if (document.body?.hasClass("testApp") == true) {
|
return if (document.body?.hasClass("application") == true) {
|
||||||
val application = JSRootDemoApp()
|
val application = JSRootDemoApp()
|
||||||
|
|
||||||
@Suppress("UnsafeCastFromDynamic")
|
@Suppress("UnsafeCastFromDynamic")
|
||||||
|
@ -6,9 +6,9 @@
|
|||||||
<title>Three js demo for particle physics</title>
|
<title>Three js demo for particle physics</title>
|
||||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
|
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
|
||||||
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
|
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
|
||||||
<script type="text/javascript" src="dataforge-vis-spatial-gdml-0.1.0-dev.js"></script>
|
<script type="text/javascript" src="visionforge-spatial-gdml-0.1.0-dev.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body class="testApp">
|
<body class="application">
|
||||||
<div class="container" id="drop_zone" data-toggle="tooltip" data-placement="right"
|
<div class="container" id="drop_zone" data-toggle="tooltip" data-placement="right"
|
||||||
title="Для загрузки данных в текстовом формате, надо перетащить файл сюда">
|
title="Для загрузки данных в текстовом формате, надо перетащить файл сюда">
|
||||||
Загрузить данные
|
Загрузить данные
|
||||||
|
@ -1,81 +0,0 @@
|
|||||||
package hep.dataforge.vis.spatial
|
|
||||||
|
|
||||||
import hep.dataforge.context.AbstractPlugin
|
|
||||||
import hep.dataforge.context.Context
|
|
||||||
import hep.dataforge.context.PluginFactory
|
|
||||||
import hep.dataforge.context.PluginTag
|
|
||||||
import hep.dataforge.meta.*
|
|
||||||
import hep.dataforge.names.Name
|
|
||||||
import hep.dataforge.names.toName
|
|
||||||
import hep.dataforge.vis.SimpleVisualGroup
|
|
||||||
import hep.dataforge.vis.Visual
|
|
||||||
import hep.dataforge.vis.VisualObject
|
|
||||||
import kotlinx.serialization.UnstableDefault
|
|
||||||
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 Visual3D(meta: Meta) : AbstractPlugin(meta) {
|
|
||||||
|
|
||||||
val visual by require(Visual)
|
|
||||||
|
|
||||||
override val tag: PluginTag get() = Companion.tag
|
|
||||||
|
|
||||||
override fun provideTop(target: String): Map<Name, Any> = if (target == Visual.VISUAL_FACTORY_TYPE) {
|
|
||||||
mapOf(Box.TYPE_NAME.toName() to Box)
|
|
||||||
} else {
|
|
||||||
super.provideTop(target)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
companion object : PluginFactory<Visual3D> {
|
|
||||||
override val tag: PluginTag = PluginTag(name = "visual.spatial", group = PluginTag.DATAFORGE_GROUP)
|
|
||||||
override val type: KClass<out Visual3D> = Visual3D::class
|
|
||||||
override fun invoke(meta: Meta, context: Context): Visual3D = Visual3D(meta)
|
|
||||||
|
|
||||||
val serialModule = SerializersModule {
|
|
||||||
contextual(Point3DSerializer)
|
|
||||||
contextual(Point2DSerializer)
|
|
||||||
|
|
||||||
polymorphic(VisualObject::class, VisualObject3D::class) {
|
|
||||||
subclass(SimpleVisualGroup.serializer())
|
|
||||||
subclass(VisualGroup3D.serializer())
|
|
||||||
subclass(Proxy.serializer())
|
|
||||||
subclass(Composite.serializer())
|
|
||||||
subclass(Tube.serializer())
|
|
||||||
subclass(Box.serializer())
|
|
||||||
subclass(Convex.serializer())
|
|
||||||
subclass(Extruded.serializer())
|
|
||||||
subclass(PolyLine.serializer())
|
|
||||||
subclass(Label3D.serializer())
|
|
||||||
subclass(Sphere.serializer())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(UnstableDefault::class)
|
|
||||||
internal val json = Json(
|
|
||||||
JsonConfiguration(
|
|
||||||
prettyPrint = true,
|
|
||||||
useArrayPolymorphism = false,
|
|
||||||
encodeDefaults = false,
|
|
||||||
ignoreUnknownKeys = true
|
|
||||||
),
|
|
||||||
context = serialModule
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun VisualObject3D.update(meta: Meta) {
|
|
||||||
fun Meta.toVector(default: Float = 0f) = Point3D(
|
|
||||||
this[VisualObject3D.x].float ?: default,
|
|
||||||
this[VisualObject3D.y].float ?: default,
|
|
||||||
this[VisualObject3D.z].float ?: default
|
|
||||||
)
|
|
||||||
|
|
||||||
meta[VisualObject3D.position].node?.toVector()?.let { position = it }
|
|
||||||
meta[VisualObject3D.rotation].node?.toVector()?.let { rotation = it }
|
|
||||||
meta[VisualObject3D.scale].node?.toVector(1f)?.let { scale = it }
|
|
||||||
meta["properties"].node?.let { configure(it) }
|
|
||||||
}
|
|
@ -1,204 +0,0 @@
|
|||||||
@file:UseSerializers(Point3DSerializer::class)
|
|
||||||
|
|
||||||
package hep.dataforge.vis.spatial
|
|
||||||
|
|
||||||
import hep.dataforge.meta.*
|
|
||||||
import hep.dataforge.meta.descriptors.NodeDescriptor
|
|
||||||
import hep.dataforge.names.asName
|
|
||||||
import hep.dataforge.names.plus
|
|
||||||
import hep.dataforge.output.Renderer
|
|
||||||
import hep.dataforge.values.ValueType
|
|
||||||
import hep.dataforge.values.asValue
|
|
||||||
import hep.dataforge.vis.VisualObject
|
|
||||||
import hep.dataforge.vis.spatial.VisualObject3D.Companion.DETAIL_KEY
|
|
||||||
import hep.dataforge.vis.spatial.VisualObject3D.Companion.IGNORE_KEY
|
|
||||||
import hep.dataforge.vis.spatial.VisualObject3D.Companion.LAYER_KEY
|
|
||||||
import hep.dataforge.vis.spatial.VisualObject3D.Companion.VISIBLE_KEY
|
|
||||||
import kotlinx.serialization.UseSerializers
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface for 3-dimensional [VisualObject]
|
|
||||||
*/
|
|
||||||
interface VisualObject3D : VisualObject {
|
|
||||||
var position: Point3D?
|
|
||||||
var rotation: Point3D?
|
|
||||||
var scale: Point3D?
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
|
|
||||||
val VISIBLE_KEY = "visible".asName()
|
|
||||||
|
|
||||||
// val SELECTED_KEY = "selected".asName()
|
|
||||||
val DETAIL_KEY = "detail".asName()
|
|
||||||
val LAYER_KEY = "layer".asName()
|
|
||||||
val IGNORE_KEY = "ignore".asName()
|
|
||||||
|
|
||||||
val GEOMETRY_KEY = "geometry".asName()
|
|
||||||
|
|
||||||
val x = "x".asName()
|
|
||||||
val y = "y".asName()
|
|
||||||
val z = "z".asName()
|
|
||||||
|
|
||||||
val position = "pos".asName()
|
|
||||||
|
|
||||||
val xPos = position + x
|
|
||||||
val yPos = position + y
|
|
||||||
val zPos = position + z
|
|
||||||
|
|
||||||
val rotation = "rotation".asName()
|
|
||||||
|
|
||||||
val xRotation = rotation + x
|
|
||||||
val yRotation = rotation + y
|
|
||||||
val zRotation = rotation + z
|
|
||||||
|
|
||||||
val rotationOrder = rotation + "order"
|
|
||||||
|
|
||||||
val scale = "scale".asName()
|
|
||||||
|
|
||||||
val xScale = scale + x
|
|
||||||
val yScale = scale + y
|
|
||||||
val zScale = scale + z
|
|
||||||
|
|
||||||
val descriptor by lazy {
|
|
||||||
NodeDescriptor {
|
|
||||||
defineValue(VISIBLE_KEY) {
|
|
||||||
type(ValueType.BOOLEAN)
|
|
||||||
default(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
defineItem(Material3D.MATERIAL_KEY.toString(), Material3D.descriptor)
|
|
||||||
|
|
||||||
// Material3D.MATERIAL_COLOR_KEY put "#ffffff"
|
|
||||||
// Material3D.MATERIAL_OPACITY_KEY put 1.0
|
|
||||||
// Material3D.MATERIAL_WIREFRAME_KEY put false
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Count number of layers to the top object. Return 1 if this is top layer
|
|
||||||
*/
|
|
||||||
var VisualObject3D.layer: Int
|
|
||||||
get() = getProperty(LAYER_KEY).int ?: 0
|
|
||||||
set(value) {
|
|
||||||
setProperty(LAYER_KEY, value.asValue())
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Renderer<VisualObject3D>.render(meta: Meta = Meta.EMPTY, action: VisualGroup3D.() -> Unit) =
|
|
||||||
render(VisualGroup3D().apply(action), meta)
|
|
||||||
|
|
||||||
// Common properties
|
|
||||||
|
|
||||||
enum class RotationOrder {
|
|
||||||
XYZ,
|
|
||||||
YZX,
|
|
||||||
ZXY,
|
|
||||||
XZY,
|
|
||||||
YXZ,
|
|
||||||
ZYX
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Rotation order
|
|
||||||
*/
|
|
||||||
var VisualObject3D.rotationOrder: RotationOrder
|
|
||||||
get() = getProperty(VisualObject3D.rotationOrder).enum<RotationOrder>() ?: RotationOrder.XYZ
|
|
||||||
set(value) = setProperty(VisualObject3D.rotationOrder, value.name.asValue())
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Preferred number of polygons for displaying the object. If not defined, uses shape or renderer default. Not inherited
|
|
||||||
*/
|
|
||||||
var VisualObject3D.detail: Int?
|
|
||||||
get() = getProperty(DETAIL_KEY, false).int
|
|
||||||
set(value) = setProperty(DETAIL_KEY, value?.asValue())
|
|
||||||
|
|
||||||
var VisualObject.visible: Boolean?
|
|
||||||
get() = getProperty(VISIBLE_KEY).boolean
|
|
||||||
set(value) = setProperty(VISIBLE_KEY, value?.asValue())
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If this property is true, the object will be ignored on render.
|
|
||||||
* Property is not inherited.
|
|
||||||
*/
|
|
||||||
var VisualObject.ignore: Boolean?
|
|
||||||
get() = getProperty(IGNORE_KEY, false).boolean
|
|
||||||
set(value) = setProperty(IGNORE_KEY, value?.asValue())
|
|
||||||
|
|
||||||
//var VisualObject.selected: Boolean?
|
|
||||||
// get() = getProperty(SELECTED_KEY).boolean
|
|
||||||
// set(value) = setProperty(SELECTED_KEY, value)
|
|
||||||
|
|
||||||
private fun VisualObject3D.position(): Point3D =
|
|
||||||
position ?: Point3D(0.0, 0.0, 0.0).also { position = it }
|
|
||||||
|
|
||||||
var VisualObject3D.x: Number
|
|
||||||
get() = position?.x ?: 0f
|
|
||||||
set(value) {
|
|
||||||
position().x = value.toDouble()
|
|
||||||
propertyInvalidated(VisualObject3D.xPos)
|
|
||||||
}
|
|
||||||
|
|
||||||
var VisualObject3D.y: Number
|
|
||||||
get() = position?.y ?: 0f
|
|
||||||
set(value) {
|
|
||||||
position().y = value.toDouble()
|
|
||||||
propertyInvalidated(VisualObject3D.yPos)
|
|
||||||
}
|
|
||||||
|
|
||||||
var VisualObject3D.z: Number
|
|
||||||
get() = position?.z ?: 0f
|
|
||||||
set(value) {
|
|
||||||
position().z = value.toDouble()
|
|
||||||
propertyInvalidated(VisualObject3D.zPos)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun VisualObject3D.rotation(): Point3D =
|
|
||||||
rotation ?: Point3D(0.0, 0.0, 0.0).also { rotation = it }
|
|
||||||
|
|
||||||
var VisualObject3D.rotationX: Number
|
|
||||||
get() = rotation?.x ?: 0f
|
|
||||||
set(value) {
|
|
||||||
rotation().x = value.toDouble()
|
|
||||||
propertyInvalidated(VisualObject3D.xRotation)
|
|
||||||
}
|
|
||||||
|
|
||||||
var VisualObject3D.rotationY: Number
|
|
||||||
get() = rotation?.y ?: 0f
|
|
||||||
set(value) {
|
|
||||||
rotation().y = value.toDouble()
|
|
||||||
propertyInvalidated(VisualObject3D.yRotation)
|
|
||||||
}
|
|
||||||
|
|
||||||
var VisualObject3D.rotationZ: Number
|
|
||||||
get() = rotation?.z ?: 0f
|
|
||||||
set(value) {
|
|
||||||
rotation().z = value.toDouble()
|
|
||||||
propertyInvalidated(VisualObject3D.zRotation)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun VisualObject3D.scale(): Point3D =
|
|
||||||
scale ?: Point3D(1.0, 1.0, 1.0).also { scale = it }
|
|
||||||
|
|
||||||
var VisualObject3D.scaleX: Number
|
|
||||||
get() = scale?.x ?: 1f
|
|
||||||
set(value) {
|
|
||||||
scale().x = value.toDouble()
|
|
||||||
propertyInvalidated(VisualObject3D.xScale)
|
|
||||||
}
|
|
||||||
|
|
||||||
var VisualObject3D.scaleY: Number
|
|
||||||
get() = scale?.y ?: 1f
|
|
||||||
set(value) {
|
|
||||||
scale().y = value.toDouble()
|
|
||||||
propertyInvalidated(VisualObject3D.yScale)
|
|
||||||
}
|
|
||||||
|
|
||||||
var VisualObject3D.scaleZ: Number
|
|
||||||
get() = scale?.z ?: 1f
|
|
||||||
set(value) {
|
|
||||||
scale().z = value.toDouble()
|
|
||||||
propertyInvalidated(VisualObject3D.zScale)
|
|
||||||
}
|
|
@ -1,56 +0,0 @@
|
|||||||
package hep.dataforge.vis.spatial.three
|
|
||||||
|
|
||||||
import hep.dataforge.context.Global
|
|
||||||
import hep.dataforge.vis.spatial.VisualObject3D
|
|
||||||
import hep.dataforge.vis.spatial.specifications.Canvas
|
|
||||||
import kotlinx.html.id
|
|
||||||
import org.w3c.dom.HTMLElement
|
|
||||||
import react.RBuilder
|
|
||||||
import react.RComponent
|
|
||||||
import react.RProps
|
|
||||||
import react.RState
|
|
||||||
import react.dom.div
|
|
||||||
import kotlin.browser.document
|
|
||||||
import kotlin.dom.clear
|
|
||||||
|
|
||||||
interface ThreeCanvasProps : RProps {
|
|
||||||
var obj: VisualObject3D
|
|
||||||
var canvasId: String
|
|
||||||
var options: Canvas
|
|
||||||
}
|
|
||||||
|
|
||||||
class ThreeCanvasComponent : RComponent<ThreeCanvasProps, RState>() {
|
|
||||||
|
|
||||||
private val three: ThreePlugin = Global.plugins.fetch(ThreePlugin)
|
|
||||||
|
|
||||||
override fun componentDidMount() {
|
|
||||||
val element = document.getElementById(props.canvasId) as? HTMLElement
|
|
||||||
?: error("Element with id 'canvas' not found on page")
|
|
||||||
val output = three.output(element, props.options)
|
|
||||||
output.render(props.obj)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun componentWillUnmount() {
|
|
||||||
val element = document.getElementById(props.canvasId) as? HTMLElement
|
|
||||||
?: error("Element with id 'canvas' not found on page")
|
|
||||||
element.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun RBuilder.render() {
|
|
||||||
div {
|
|
||||||
attrs {
|
|
||||||
id = props.canvasId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun RBuilder.threeCanvas(object3D: VisualObject3D, id: String = "threeCanvas", options: Canvas.() -> Unit = {}) {
|
|
||||||
child(ThreeCanvasComponent::class) {
|
|
||||||
attrs {
|
|
||||||
this.obj = object3D
|
|
||||||
this.canvasId = id
|
|
||||||
this.options = Canvas.invoke(options)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
package hep.dataforge.vis.spatial.three
|
|
||||||
|
|
||||||
import hep.dataforge.js.jsObject
|
|
||||||
import hep.dataforge.meta.MetaItem
|
|
||||||
import hep.dataforge.names.Name
|
|
||||||
import hep.dataforge.vis.spatial.Label3D
|
|
||||||
import hep.dataforge.vis.spatial.three.ThreeMaterials.getMaterial
|
|
||||||
import info.laht.threekt.core.Object3D
|
|
||||||
import info.laht.threekt.geometries.TextBufferGeometry
|
|
||||||
import info.laht.threekt.objects.Mesh
|
|
||||||
import kotlin.reflect.KClass
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
object ThreeLabelFactory : ThreeFactory<Label3D> {
|
|
||||||
override val type: KClass<in Label3D> get() = Label3D::class
|
|
||||||
|
|
||||||
override fun invoke(obj: Label3D): Object3D {
|
|
||||||
val textGeo = TextBufferGeometry( obj.text, jsObject {
|
|
||||||
font = obj.fontFamily
|
|
||||||
size = 20
|
|
||||||
height = 1
|
|
||||||
curveSegments = 1
|
|
||||||
} )
|
|
||||||
return Mesh(textGeo, getMaterial(obj)).apply {
|
|
||||||
updatePosition(obj)
|
|
||||||
obj.onPropertyChange(this@ThreeLabelFactory){ _: Name, _: MetaItem<*>?, _: MetaItem<*>? ->
|
|
||||||
//TODO
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
15
demo/gdml/README.md
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
|
||||||
|
### GDML Example
|
||||||
|
|
||||||
|
Visualization example for geometry defined as GDML file.
|
||||||
|
|
||||||
|
##### Building project
|
||||||
|
|
||||||
|
To build the app, run `demo/gdml/Tasks/distribution/jsBrowserDistribution` Gradle task, then open
|
||||||
|
`demo/gdml/build/distribuions/gdml-js-0.1.3-dev/index.html` file in your browser, and
|
||||||
|
drag-and-drop GDML file to the window to see visualization. For an example file, you can use
|
||||||
|
`demo/gdml/src/jsMain/resources/cubes.gdml`.
|
||||||
|
|
||||||
|
##### Example view:
|
||||||
|
|
||||||
|
![](../../doc/resources/gdml-demo.png)
|
@ -1,6 +1,6 @@
|
|||||||
import scientifik.DependencyConfiguration
|
import scientifik.DependencyConfiguration
|
||||||
import scientifik.FXModule
|
import scientifik.FXModule
|
||||||
import scientifik.fx
|
import scientifik.useFx
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("scientifik.mpp")
|
id("scientifik.mpp")
|
||||||
@ -8,7 +8,7 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val fxVersion: String by rootProject.extra
|
val fxVersion: String by rootProject.extra
|
||||||
fx(FXModule.CONTROLS, version = fxVersion, configuration = DependencyConfiguration.IMPLEMENTATION)
|
useFx(FXModule.CONTROLS, version = fxVersion, configuration = DependencyConfiguration.IMPLEMENTATION)
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
|
|
||||||
@ -17,23 +17,31 @@ kotlin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
js {
|
js {
|
||||||
browser {
|
useCommonJs()
|
||||||
webpackTask {
|
|
||||||
//sourceMaps = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
commonMain {
|
commonMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
api(project(":dataforge-vis-spatial"))
|
implementation(project(":visionforge-solid"))
|
||||||
api(project(":dataforge-vis-spatial-gdml"))
|
implementation(project(":visionforge-gdml"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
jsMain{
|
||||||
|
dependencies {
|
||||||
|
implementation(project(":ui:bootstrap"))
|
||||||
|
implementation(npm("react-file-drop", "3.0.6"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
application {
|
application {
|
||||||
mainClassName = "hep.dataforge.vis.spatial.gdml.demo.GDMLDemoAppKt"
|
mainClassName = "hep.dataforge.vision.gdml.demo.GDMLDemoAppKt"
|
||||||
|
}
|
||||||
|
|
||||||
|
val convertGdmlToJson by tasks.creating(JavaExec::class) {
|
||||||
|
group = "application"
|
||||||
|
classpath = sourceSets["main"].runtimeClasspath
|
||||||
|
main = "hep.dataforge.vis.spatial.gdml.demo.SaveToJsonKt"
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package hep.dataforge.vis.spatial.gdml.demo
|
package hep.dataforge.vision.gdml.demo
|
||||||
|
|
||||||
import scientifik.gdml.*
|
import scientifik.gdml.*
|
||||||
|
|
||||||
@ -12,7 +12,7 @@ fun cubes(): GDML = GDML {
|
|||||||
val segment = solids.tube("segment", 20, 5.0) {
|
val segment = solids.tube("segment", 20, 5.0) {
|
||||||
rmin = 17
|
rmin = 17
|
||||||
deltaphi = 60
|
deltaphi = 60
|
||||||
aunit = DEG
|
aunit = AUnit.DEG.title
|
||||||
}
|
}
|
||||||
val worldBox = solids.box("LargeBox", 200, 200, 200)
|
val worldBox = solids.box("LargeBox", 200, 200, 200)
|
||||||
val smallBox = solids.box("smallBox", 30, 30, 30)
|
val smallBox = solids.box("smallBox", 30, 30, 30)
|
||||||
@ -24,7 +24,7 @@ fun cubes(): GDML = GDML {
|
|||||||
positionref = center.ref()
|
positionref = center.ref()
|
||||||
rotation {
|
rotation {
|
||||||
z = 60 * i
|
z = 60 * i
|
||||||
unit = DEG
|
unit = AUnit.DEG.title
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,20 +0,0 @@
|
|||||||
package hep.dataforge.vis.spatial.gdml
|
|
||||||
|
|
||||||
import hep.dataforge.meta.setProperty
|
|
||||||
import hep.dataforge.meta.string
|
|
||||||
import hep.dataforge.names.toName
|
|
||||||
import hep.dataforge.values.asValue
|
|
||||||
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".asValue())
|
|
||||||
assertEquals("red", visual["composite000.segment0".toName()]?.getProperty(Material3D.MATERIAL_COLOR_KEY).string)
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,20 @@
|
|||||||
|
package hep.dataforge.vision.gdml
|
||||||
|
|
||||||
|
import hep.dataforge.meta.setItem
|
||||||
|
import hep.dataforge.meta.string
|
||||||
|
import hep.dataforge.names.toName
|
||||||
|
import hep.dataforge.values.asValue
|
||||||
|
import hep.dataforge.vision.gdml.demo.cubes
|
||||||
|
import hep.dataforge.vision.solid.SolidMaterial
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
class GDMLVisualTest {
|
||||||
|
@Test
|
||||||
|
fun testPrototypeProperty() {
|
||||||
|
val gdml = cubes()
|
||||||
|
val visual = gdml.toVision()
|
||||||
|
visual["composite000.segment0".toName()]?.setItem(SolidMaterial.MATERIAL_COLOR_KEY, "red".asValue())
|
||||||
|
assertEquals("red", visual["composite000.segment0".toName()]?.getItem(SolidMaterial.MATERIAL_COLOR_KEY).string)
|
||||||
|
}
|
||||||
|
}
|
40
demo/gdml/src/jsMain/kotlin/drop/FileDrop.kt
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
@file:JsModule("react-file-drop")
|
||||||
|
@file:JsNonModule
|
||||||
|
|
||||||
|
package drop
|
||||||
|
|
||||||
|
import org.w3c.dom.DragEvent
|
||||||
|
import org.w3c.files.FileList
|
||||||
|
import react.*
|
||||||
|
|
||||||
|
external enum class DropEffects {
|
||||||
|
copy,
|
||||||
|
move,
|
||||||
|
link,
|
||||||
|
none
|
||||||
|
}
|
||||||
|
|
||||||
|
external interface FileDropProps: RProps {
|
||||||
|
var className: String?
|
||||||
|
var targetClassName: String?
|
||||||
|
var draggingOverFrameClassName: String?
|
||||||
|
var draggingOverTargetClassName: String?
|
||||||
|
|
||||||
|
// var frame?: Exclude<HTMLElementTagNameMap[keyof HTMLElementTagNameMap], HTMLElement> | HTMLDocument;
|
||||||
|
var onFrameDragEnter: ((event: DragEvent) -> Unit)?
|
||||||
|
var onFrameDragLeave: ((event: DragEvent) -> Unit)?
|
||||||
|
var onFrameDrop: ((event: DragEvent) -> Unit)?
|
||||||
|
// var onDragOver: ReactDragEventHandler<HTMLDivElement>?
|
||||||
|
// var onDragLeave: ReactDragEventHandler<HTMLDivElement>?
|
||||||
|
var onDrop: ((files: FileList?, event: dynamic) -> Unit)?//event:DragEvent<HTMLDivElement>)
|
||||||
|
var dropEffect: DropEffects?
|
||||||
|
}
|
||||||
|
|
||||||
|
external interface FileDropState: RState {
|
||||||
|
var draggingOverFrame: Boolean
|
||||||
|
var draggingOverTarget: Boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
external class FileDrop : Component<FileDropProps, FileDropState> {
|
||||||
|
override fun render(): dynamic
|
||||||
|
}
|
@ -1,223 +0,0 @@
|
|||||||
package hep.dataforge.vis.spatial.gdml.demo
|
|
||||||
|
|
||||||
import hep.dataforge.context.Global
|
|
||||||
import hep.dataforge.js.Application
|
|
||||||
import hep.dataforge.js.startApplication
|
|
||||||
import hep.dataforge.names.Name
|
|
||||||
import hep.dataforge.names.isEmpty
|
|
||||||
import hep.dataforge.vis.VisualGroup
|
|
||||||
import hep.dataforge.vis.VisualObject
|
|
||||||
import hep.dataforge.vis.editor.objectTree
|
|
||||||
import hep.dataforge.vis.editor.visualPropertyEditor
|
|
||||||
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.VisualGroup3D
|
|
||||||
import hep.dataforge.vis.spatial.VisualObject3D
|
|
||||||
import hep.dataforge.vis.spatial.VisualObject3D.Companion.VISIBLE_KEY
|
|
||||||
import hep.dataforge.vis.spatial.gdml.GDMLTransformer
|
|
||||||
import hep.dataforge.vis.spatial.gdml.LUnit
|
|
||||||
import hep.dataforge.vis.spatial.gdml.toVisual
|
|
||||||
import hep.dataforge.vis.spatial.three.ThreePlugin
|
|
||||||
import hep.dataforge.vis.spatial.three.displayCanvasControls
|
|
||||||
import hep.dataforge.vis.spatial.three.output
|
|
||||||
import org.w3c.dom.*
|
|
||||||
import org.w3c.files.FileList
|
|
||||||
import org.w3c.files.FileReader
|
|
||||||
import org.w3c.files.get
|
|
||||||
import scientifik.gdml.GDML
|
|
||||||
import kotlin.browser.document
|
|
||||||
import kotlin.browser.window
|
|
||||||
import kotlin.dom.clear
|
|
||||||
|
|
||||||
private class GDMLDemoApp : Application {
|
|
||||||
/**
|
|
||||||
* Handle mouse drag according to https://www.html5rocks.com/en/tutorials/file/dndfiles/
|
|
||||||
*/
|
|
||||||
private fun handleDragOver(event: DragEvent) {
|
|
||||||
event.stopPropagation()
|
|
||||||
event.preventDefault()
|
|
||||||
event.dataTransfer?.dropEffect = "copy"
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load data from text file
|
|
||||||
*/
|
|
||||||
private fun loadData(event: DragEvent, block: (name: String, data: String) -> Unit) {
|
|
||||||
event.stopPropagation()
|
|
||||||
event.preventDefault()
|
|
||||||
|
|
||||||
val file = (event.dataTransfer?.files as FileList)[0]
|
|
||||||
?: throw RuntimeException("Failed to load file")
|
|
||||||
|
|
||||||
FileReader().apply {
|
|
||||||
onload = {
|
|
||||||
val string = result as String
|
|
||||||
block(file.name, string)
|
|
||||||
}
|
|
||||||
readAsText(file)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun spinner(show: Boolean) {
|
|
||||||
// if( show){
|
|
||||||
//
|
|
||||||
// val style = if (show) {
|
|
||||||
// "display:block;"
|
|
||||||
// } else {
|
|
||||||
// "display:none;"
|
|
||||||
// }
|
|
||||||
// document.getElementById("canvas")?.append {
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun message(message: String?) {
|
|
||||||
console.log(message)
|
|
||||||
// document.getElementById("messages")?.let { element ->
|
|
||||||
// if (message == null) {
|
|
||||||
// element.clear()
|
|
||||||
// } else {
|
|
||||||
// element.append {
|
|
||||||
// p {
|
|
||||||
// +message
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
private val gdmlConfiguration: GDMLTransformer.() -> Unit = {
|
|
||||||
lUnit = LUnit.CM
|
|
||||||
volumeAction = { volume ->
|
|
||||||
when {
|
|
||||||
volume.name.startsWith("ecal01lay") -> GDMLTransformer.Action.REJECT
|
|
||||||
volume.name.startsWith("UPBL") -> GDMLTransformer.Action.REJECT
|
|
||||||
volume.name.startsWith("USCL") -> GDMLTransformer.Action.REJECT
|
|
||||||
volume.name.startsWith("VPBL") -> GDMLTransformer.Action.REJECT
|
|
||||||
volume.name.startsWith("VSCL") -> GDMLTransformer.Action.REJECT
|
|
||||||
else -> GDMLTransformer.Action.CACHE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
solidConfiguration = { parent, solid ->
|
|
||||||
if (
|
|
||||||
solid.name.startsWith("Yoke")
|
|
||||||
|| solid.name.startsWith("Pole")
|
|
||||||
|| parent.physVolumes.isNotEmpty()
|
|
||||||
) {
|
|
||||||
useStyle("opaque") {
|
|
||||||
MATERIAL_OPACITY_KEY put 0.3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
override fun start(state: Map<String, Any>) {
|
|
||||||
|
|
||||||
val context = Global.context("demo") {}
|
|
||||||
val three = context.plugins.load(ThreePlugin)
|
|
||||||
//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("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()
|
|
||||||
|
|
||||||
val action: (name: String, data: String) -> Unit = { name, data ->
|
|
||||||
canvasElement.clear()
|
|
||||||
spinner(true)
|
|
||||||
val visual: VisualObject3D = when {
|
|
||||||
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.parseJson(data)
|
|
||||||
else -> {
|
|
||||||
window.alert("File extension is not recognized: $name")
|
|
||||||
error("File extension is not recognized: $name")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Optimize tree
|
|
||||||
//(visual as? VisualGroup3D)?.transformInPlace(UnRef, RemoveSingleChild)
|
|
||||||
|
|
||||||
message("Rendering")
|
|
||||||
|
|
||||||
//output.camera.layers.enable(1)
|
|
||||||
val canvas = three.output(canvasElement as HTMLElement)
|
|
||||||
|
|
||||||
canvas.camera.layers.set(0)
|
|
||||||
configElement.displayCanvasControls(canvas)
|
|
||||||
//tree.visualObjectTree(visual, editor::propertyEditor)
|
|
||||||
fun selectElement(name: Name) {
|
|
||||||
val child: VisualObject = when {
|
|
||||||
name.isEmpty() -> visual
|
|
||||||
visual is VisualGroup -> visual[name] ?: return
|
|
||||||
else -> return
|
|
||||||
}
|
|
||||||
|
|
||||||
editorElement.visualPropertyEditor(name, child) {
|
|
||||||
VISIBLE_KEY put true
|
|
||||||
if (child is VisualObject3D) {
|
|
||||||
MATERIAL_COLOR_KEY put "#ffffff"
|
|
||||||
MATERIAL_OPACITY_KEY put 1.0
|
|
||||||
MATERIAL_WIREFRAME_KEY put false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// editorElement.displayPropertyEditor(name, child) { item ->
|
|
||||||
// //val descriptorMeta = Material3D.descriptor
|
|
||||||
//
|
|
||||||
// val properties = item.allProperties()
|
|
||||||
// val bottom = Meta {
|
|
||||||
// VISIBLE_KEY put (item.visible ?: true)
|
|
||||||
// if (item is VisualObject3D) {
|
|
||||||
// MATERIAL_COLOR_KEY put "#ffffff"
|
|
||||||
// MATERIAL_OPACITY_KEY put 1.0
|
|
||||||
// MATERIAL_WIREFRAME_KEY put false
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// properties.withBottom(bottom)
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
// canvas.clickListener = ::selectElement
|
|
||||||
|
|
||||||
//tree.visualObjectTree(visual, editor::propertyEditor)
|
|
||||||
treeElement.objectTree(visual) { treeName ->
|
|
||||||
selectElement(treeName)
|
|
||||||
canvas.highlight(treeName)
|
|
||||||
}
|
|
||||||
canvas.render(visual)
|
|
||||||
message(null)
|
|
||||||
spinner(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
(document.getElementById("drop_zone") as? HTMLDivElement)?.apply {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun main() {
|
|
||||||
startApplication(::GDMLDemoApp)
|
|
||||||
}
|
|
@ -0,0 +1,154 @@
|
|||||||
|
package hep.dataforge.vision.gdml.demo
|
||||||
|
|
||||||
|
import hep.dataforge.context.Context
|
||||||
|
import hep.dataforge.names.Name
|
||||||
|
import hep.dataforge.names.isEmpty
|
||||||
|
import hep.dataforge.vision.Vision
|
||||||
|
import hep.dataforge.vision.VisionGroup
|
||||||
|
import hep.dataforge.vision.bootstrap.*
|
||||||
|
import hep.dataforge.vision.gdml.toVision
|
||||||
|
import hep.dataforge.vision.react.component
|
||||||
|
import hep.dataforge.vision.react.configEditor
|
||||||
|
import hep.dataforge.vision.react.flexColumn
|
||||||
|
import hep.dataforge.vision.react.state
|
||||||
|
import hep.dataforge.vision.solid.Solid
|
||||||
|
import hep.dataforge.vision.solid.SolidGroup
|
||||||
|
import hep.dataforge.vision.solid.specifications.Camera
|
||||||
|
import hep.dataforge.vision.solid.specifications.Canvas3DOptions
|
||||||
|
import hep.dataforge.vision.solid.three.ThreeCanvas
|
||||||
|
import hep.dataforge.vision.solid.three.ThreeCanvasComponent
|
||||||
|
import hep.dataforge.vision.solid.three.canvasControls
|
||||||
|
import kotlinx.css.FlexBasis
|
||||||
|
import kotlinx.css.Overflow
|
||||||
|
import kotlinx.css.flex
|
||||||
|
import kotlinx.css.overflow
|
||||||
|
import org.w3c.files.FileReader
|
||||||
|
import org.w3c.files.get
|
||||||
|
import react.RProps
|
||||||
|
import react.dom.h1
|
||||||
|
import scientifik.gdml.GDML
|
||||||
|
import scientifik.gdml.parse
|
||||||
|
import styled.css
|
||||||
|
import styled.styledDiv
|
||||||
|
import kotlin.browser.window
|
||||||
|
import kotlin.math.PI
|
||||||
|
|
||||||
|
interface GDMLAppProps : RProps {
|
||||||
|
var context: Context
|
||||||
|
var rootObject: Vision?
|
||||||
|
var selected: Name?
|
||||||
|
}
|
||||||
|
|
||||||
|
private val canvasConfig = Canvas3DOptions {
|
||||||
|
camera = Camera {
|
||||||
|
distance = 2100.0
|
||||||
|
latitude = PI / 6
|
||||||
|
azimuth = PI + PI / 6
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val GDMLApp = component<GDMLAppProps> { props ->
|
||||||
|
var selected by state { props.selected }
|
||||||
|
var canvas: ThreeCanvas? by state { null }
|
||||||
|
var visual: Vision? by state { props.rootObject }
|
||||||
|
|
||||||
|
val select: (Name?) -> Unit = {
|
||||||
|
selected = it
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadData(name: String, data: String) {
|
||||||
|
visual = when {
|
||||||
|
name.endsWith(".gdml") || name.endsWith(".xml") -> {
|
||||||
|
val gdml = GDML.parse(data)
|
||||||
|
gdml.toVision(gdmlConfiguration)
|
||||||
|
}
|
||||||
|
name.endsWith(".json") -> SolidGroup.parseJson(data)
|
||||||
|
else -> {
|
||||||
|
window.alert("File extension is not recognized: $name")
|
||||||
|
error("File extension is not recognized: $name")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flexColumn {
|
||||||
|
css {
|
||||||
|
flex(1.0, 1.0, FlexBasis.auto)
|
||||||
|
}
|
||||||
|
h1 { +"GDML/JSON loader demo" }
|
||||||
|
styledDiv {
|
||||||
|
css {
|
||||||
|
classes.add("row")
|
||||||
|
classes.add("p-1")
|
||||||
|
overflow = Overflow.auto
|
||||||
|
}
|
||||||
|
gridColumn(3, maxSize= GridMaxSize.XL, classes = "order-2 order-xl-1") {
|
||||||
|
card("Load data") {
|
||||||
|
fileDrop("(drag file here)") { files ->
|
||||||
|
val file = files?.get(0)
|
||||||
|
if (file != null) {
|
||||||
|
|
||||||
|
FileReader().apply {
|
||||||
|
onload = {
|
||||||
|
val string = result as String
|
||||||
|
loadData(file.name, string)
|
||||||
|
}
|
||||||
|
readAsText(file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//tree
|
||||||
|
card("Object tree", "overflow-auto") {
|
||||||
|
visual?.let {
|
||||||
|
objectTree(it, selected, select)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gridColumn(6, maxSize= GridMaxSize.XL, classes = "order-1 order-xl-2") {
|
||||||
|
//canvas
|
||||||
|
(visual as? Solid)?.let { visual3D ->
|
||||||
|
child(ThreeCanvasComponent::class) {
|
||||||
|
attrs {
|
||||||
|
this.context = props.context
|
||||||
|
this.obj = visual3D
|
||||||
|
this.selected = selected
|
||||||
|
this.clickCallback = select
|
||||||
|
this.canvasCallback = {
|
||||||
|
canvas = it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gridColumn(3, maxSize= GridMaxSize.XL, classes = "order-3") {
|
||||||
|
container {
|
||||||
|
//settings
|
||||||
|
canvas?.let {
|
||||||
|
card("Canvas configuration") {
|
||||||
|
canvasControls(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
container {
|
||||||
|
namecrumbs(selected, "World") { selected = it }
|
||||||
|
}
|
||||||
|
container {
|
||||||
|
//properties
|
||||||
|
card("Properties") {
|
||||||
|
selected.let { selected ->
|
||||||
|
val selectedObject: Vision? = when {
|
||||||
|
selected == null -> null
|
||||||
|
selected.isEmpty() -> visual
|
||||||
|
else -> (visual as? VisionGroup)?.get(selected)
|
||||||
|
}
|
||||||
|
if (selectedObject != null) {
|
||||||
|
configEditor(selectedObject, default = selectedObject.getAllProperties(), key = selected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,86 @@
|
|||||||
|
package hep.dataforge.vision.gdml.demo
|
||||||
|
|
||||||
|
import hep.dataforge.context.Global
|
||||||
|
import hep.dataforge.js.Application
|
||||||
|
import hep.dataforge.js.startApplication
|
||||||
|
import hep.dataforge.vision.gdml.GDMLTransformer
|
||||||
|
import hep.dataforge.vision.gdml.LUnit
|
||||||
|
import hep.dataforge.vision.gdml.toVision
|
||||||
|
import hep.dataforge.vision.solid.SolidMaterial.Companion.MATERIAL_OPACITY_KEY
|
||||||
|
import kotlinx.css.*
|
||||||
|
import react.child
|
||||||
|
import react.dom.render
|
||||||
|
import styled.injectGlobal
|
||||||
|
import kotlin.browser.document
|
||||||
|
|
||||||
|
|
||||||
|
val gdmlConfiguration: GDMLTransformer.() -> Unit = {
|
||||||
|
lUnit = LUnit.CM
|
||||||
|
volumeAction = { volume ->
|
||||||
|
when {
|
||||||
|
volume.name.startsWith("ecal01lay") -> GDMLTransformer.Action.REJECT
|
||||||
|
volume.name.startsWith("UPBL") -> GDMLTransformer.Action.REJECT
|
||||||
|
volume.name.startsWith("USCL") -> GDMLTransformer.Action.REJECT
|
||||||
|
volume.name.startsWith("VPBL") -> GDMLTransformer.Action.REJECT
|
||||||
|
volume.name.startsWith("VSCL") -> GDMLTransformer.Action.REJECT
|
||||||
|
else -> GDMLTransformer.Action.CACHE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
solidConfiguration = { parent, solid ->
|
||||||
|
if (
|
||||||
|
solid.name.startsWith("Yoke")
|
||||||
|
|| solid.name.startsWith("Pole")
|
||||||
|
|| parent.physVolumes.isNotEmpty()
|
||||||
|
) {
|
||||||
|
useStyle("opaque") {
|
||||||
|
MATERIAL_OPACITY_KEY put 0.3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class GDMLDemoApp : Application {
|
||||||
|
|
||||||
|
override fun start(state: Map<String, Any>) {
|
||||||
|
|
||||||
|
injectGlobal {
|
||||||
|
body {
|
||||||
|
height = 100.pct
|
||||||
|
width = 100.pct
|
||||||
|
margin(0.px)
|
||||||
|
padding(0.px)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val context = Global.context("demo") {}
|
||||||
|
val element = document.getElementById("app") ?: error("Element with id 'app' not found on page")
|
||||||
|
|
||||||
|
render(element) {
|
||||||
|
child(GDMLApp) {
|
||||||
|
attrs {
|
||||||
|
this.context = context
|
||||||
|
this.rootObject = cubes().toVision(gdmlConfiguration)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// (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)
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun main() {
|
||||||
|
startApplication(::GDMLDemoApp)
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package hep.dataforge.vision.gdml.demo
|
||||||
|
|
||||||
|
import drop.FileDrop
|
||||||
|
import kotlinx.css.*
|
||||||
|
import kotlinx.css.properties.border
|
||||||
|
import org.w3c.files.FileList
|
||||||
|
import react.RBuilder
|
||||||
|
import styled.css
|
||||||
|
import styled.styledDiv
|
||||||
|
|
||||||
|
//TODO move styles to inline
|
||||||
|
|
||||||
|
fun RBuilder.fileDrop(title: String, action: (files: FileList?) -> Unit) {
|
||||||
|
styledDiv {
|
||||||
|
css {
|
||||||
|
border(style = BorderStyle.dashed, width = 1.px, color = Color.orange)
|
||||||
|
alignContent = Align.center
|
||||||
|
}
|
||||||
|
|
||||||
|
child(FileDrop::class) {
|
||||||
|
attrs {
|
||||||
|
onDrop = { files, _ ->
|
||||||
|
console.info("loaded $files")
|
||||||
|
action(files)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
+title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
62
demo/gdml/src/jsMain/resources/css/fileDrop.css
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
.file-drop {
|
||||||
|
/* relatively position the container bc the contents are absolute */
|
||||||
|
position: relative;
|
||||||
|
height: 100px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-drop > .file-drop-target {
|
||||||
|
/* basic styles */
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
border-radius: 2px;
|
||||||
|
|
||||||
|
/* horizontally and vertically center all content */
|
||||||
|
display: flex;
|
||||||
|
display: -webkit-box;
|
||||||
|
display: -webkit-flex;
|
||||||
|
display: -ms-flexbox;
|
||||||
|
|
||||||
|
flex-direction: column;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-webkit-box-direction: normal;
|
||||||
|
-webkit-flex-direction: column;
|
||||||
|
-ms-flex-direction: column;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
-webkit-box-align: center;
|
||||||
|
-webkit-align-items: center;
|
||||||
|
-ms-flex-align: center;
|
||||||
|
|
||||||
|
justify-content: center;
|
||||||
|
-webkit-box-pack: center;
|
||||||
|
-webkit-justify-content: center;
|
||||||
|
-ms-flex-pack: center;
|
||||||
|
|
||||||
|
align-content: center;
|
||||||
|
-webkit-align-content: center;
|
||||||
|
-ms-flex-line-pack: center;
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-drop > .file-drop-target.file-drop-dragging-over-frame {
|
||||||
|
/* overlay a black mask when dragging over the frame */
|
||||||
|
border: none;
|
||||||
|
background-color: rgba(0, 0, 0, 0.65);
|
||||||
|
box-shadow: none;
|
||||||
|
z-index: 50;
|
||||||
|
opacity: 1;
|
||||||
|
|
||||||
|
/* typography */
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-drop > .file-drop-target.file-drop-dragging-over-target {
|
||||||
|
/* turn stuff orange when we are dragging over the target */
|
||||||
|
color: #ff6e40;
|
||||||
|
box-shadow: 0 0 13px 3px #ff6e40;
|
||||||
|
}
|
@ -1,52 +0,0 @@
|
|||||||
.drop_zone {
|
|
||||||
outline: 1px solid orange;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loader {
|
|
||||||
border: 16px solid #f3f3f3; /* Light grey */
|
|
||||||
border-top: 16px solid #3498db; /* Blue */
|
|
||||||
border-radius: 50%;
|
|
||||||
width: 120px;
|
|
||||||
height: 120px;
|
|
||||||
animation: spin 2s linear infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes spin {
|
|
||||||
0% { transform: rotate(0deg); }
|
|
||||||
100% { transform: rotate(360deg); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remove default bullets */
|
|
||||||
ul, .tree {
|
|
||||||
list-style-type: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Style the caret/arrow */
|
|
||||||
.tree-caret {
|
|
||||||
cursor: pointer;
|
|
||||||
user-select: none; /* Prevent text selection */
|
|
||||||
}
|
|
||||||
|
|
||||||
.objTree-label {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create the caret/arrow with a unicode, and style it */
|
|
||||||
.tree-caret::before {
|
|
||||||
content: "\25B6";
|
|
||||||
color: black;
|
|
||||||
display: inline-block;
|
|
||||||
margin-right: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.objTree-leaf::before {
|
|
||||||
content: "\25C6";
|
|
||||||
color: black;
|
|
||||||
display: inline-block;
|
|
||||||
margin-right: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Rotate the caret/arrow icon when clicked on (using JavaScript) */
|
|
||||||
.tree-caret-down::before {
|
|
||||||
transform: rotate(90deg);
|
|
||||||
}
|
|
@ -1,748 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
width="240"
|
|
||||||
height="144"
|
|
||||||
id="svg4136"
|
|
||||||
version="1.1"
|
|
||||||
inkscape:version="0.91 r13725"
|
|
||||||
sodipodi:docname="jsoneditor-icons.svg">
|
|
||||||
<title
|
|
||||||
id="title6512">JSON Editor Icons</title>
|
|
||||||
<metadata
|
|
||||||
id="metadata4148">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title>JSON Editor Icons</dc:title>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<defs
|
|
||||||
id="defs4146" />
|
|
||||||
<sodipodi:namedview
|
|
||||||
pagecolor="#ff63ff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1"
|
|
||||||
objecttolerance="10"
|
|
||||||
gridtolerance="10"
|
|
||||||
guidetolerance="10"
|
|
||||||
inkscape:pageopacity="0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:window-width="1920"
|
|
||||||
inkscape:window-height="1026"
|
|
||||||
id="namedview4144"
|
|
||||||
showgrid="true"
|
|
||||||
inkscape:zoom="4"
|
|
||||||
inkscape:cx="13.229181"
|
|
||||||
inkscape:cy="119.82429"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="0"
|
|
||||||
inkscape:window-maximized="1"
|
|
||||||
inkscape:current-layer="svg4136"
|
|
||||||
showguides="false"
|
|
||||||
borderlayer="false"
|
|
||||||
inkscape:showpageshadow="true"
|
|
||||||
showborder="true">
|
|
||||||
<inkscape:grid
|
|
||||||
type="xygrid"
|
|
||||||
id="grid4640"
|
|
||||||
empspacing="24" />
|
|
||||||
</sodipodi:namedview>
|
|
||||||
<!-- Created with SVG-edit - http://svg-edit.googlecode.com/ -->
|
|
||||||
<rect
|
|
||||||
style="fill:#4c4c4c;fill-opacity:1;stroke:none;stroke-width:0"
|
|
||||||
id="svg_1"
|
|
||||||
height="16"
|
|
||||||
width="16"
|
|
||||||
y="4"
|
|
||||||
x="4" />
|
|
||||||
<rect
|
|
||||||
id="svg_1-7"
|
|
||||||
height="16"
|
|
||||||
width="16"
|
|
||||||
y="3.999995"
|
|
||||||
x="28.000006"
|
|
||||||
style="fill:#ec3f29;fill-opacity:0.94117647;stroke:none;stroke-width:0" />
|
|
||||||
<rect
|
|
||||||
style="fill:#4c4c4c;fill-opacity:1;stroke:none;stroke-width:0"
|
|
||||||
x="52.000004"
|
|
||||||
y="3.999995"
|
|
||||||
width="16"
|
|
||||||
height="16"
|
|
||||||
id="rect4165" />
|
|
||||||
<rect
|
|
||||||
id="rect4175"
|
|
||||||
height="16"
|
|
||||||
width="16"
|
|
||||||
y="3.9999852"
|
|
||||||
x="172.00002"
|
|
||||||
style="fill:#4c4c4c;fill-opacity:1;stroke:none;stroke-width:0" />
|
|
||||||
<rect
|
|
||||||
id="rect4175-3"
|
|
||||||
height="16"
|
|
||||||
width="16"
|
|
||||||
y="3.999995"
|
|
||||||
x="196"
|
|
||||||
style="fill:#4c4c4c;fill-opacity:1;stroke:none;stroke-width:0" />
|
|
||||||
<g
|
|
||||||
id="g4299"
|
|
||||||
style="stroke:none">
|
|
||||||
<rect
|
|
||||||
x="7.0000048"
|
|
||||||
y="10.999998"
|
|
||||||
width="9.9999924"
|
|
||||||
height="1.9999986"
|
|
||||||
id="svg_1-1"
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0" />
|
|
||||||
<rect
|
|
||||||
x="11.000005"
|
|
||||||
y="7.0000114"
|
|
||||||
width="1.9999955"
|
|
||||||
height="9.9999838"
|
|
||||||
id="svg_1-1-1"
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0" />
|
|
||||||
</g>
|
|
||||||
<g
|
|
||||||
id="g4299-3"
|
|
||||||
transform="matrix(0.70710678,-0.70710678,0.70710678,0.70710678,19.029435,12.000001)"
|
|
||||||
style="stroke:none">
|
|
||||||
<rect
|
|
||||||
x="7.0000048"
|
|
||||||
y="10.999998"
|
|
||||||
width="9.9999924"
|
|
||||||
height="1.9999986"
|
|
||||||
id="svg_1-1-0"
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0" />
|
|
||||||
<rect
|
|
||||||
x="11.000005"
|
|
||||||
y="7.0000114"
|
|
||||||
width="1.9999955"
|
|
||||||
height="9.9999838"
|
|
||||||
id="svg_1-1-1-9"
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0" />
|
|
||||||
</g>
|
|
||||||
<rect
|
|
||||||
id="svg_1-7-5"
|
|
||||||
height="6.9999905"
|
|
||||||
width="6.9999909"
|
|
||||||
y="7.0000048"
|
|
||||||
x="55.000004"
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0" />
|
|
||||||
<rect
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:#4c4c4c;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
x="58"
|
|
||||||
y="10.00001"
|
|
||||||
width="6.9999909"
|
|
||||||
height="6.9999905"
|
|
||||||
id="rect4354" />
|
|
||||||
<rect
|
|
||||||
id="svg_1-7-5-7"
|
|
||||||
height="6.9999905"
|
|
||||||
width="6.9999909"
|
|
||||||
y="10.000005"
|
|
||||||
x="58.000004"
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:#3c80df;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.94117647" />
|
|
||||||
<g
|
|
||||||
id="g4378">
|
|
||||||
<rect
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0"
|
|
||||||
x="198"
|
|
||||||
y="10.999999"
|
|
||||||
width="7.9999909"
|
|
||||||
height="1.9999965"
|
|
||||||
id="svg_1-7-5-3" />
|
|
||||||
<rect
|
|
||||||
id="rect4374"
|
|
||||||
height="1.9999946"
|
|
||||||
width="11.999995"
|
|
||||||
y="7.0000005"
|
|
||||||
x="198"
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0" />
|
|
||||||
<rect
|
|
||||||
id="rect4376"
|
|
||||||
height="1.9999995"
|
|
||||||
width="3.9999928"
|
|
||||||
y="14.999996"
|
|
||||||
x="198"
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0" />
|
|
||||||
</g>
|
|
||||||
<g
|
|
||||||
transform="matrix(1,0,0,-1,-23.999995,23.999995)"
|
|
||||||
id="g4383">
|
|
||||||
<rect
|
|
||||||
id="rect4385"
|
|
||||||
height="1.9999965"
|
|
||||||
width="7.9999909"
|
|
||||||
y="10.999999"
|
|
||||||
x="198"
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0" />
|
|
||||||
<rect
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0"
|
|
||||||
x="198"
|
|
||||||
y="7.0000005"
|
|
||||||
width="11.999995"
|
|
||||||
height="1.9999946"
|
|
||||||
id="rect4387" />
|
|
||||||
<rect
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0"
|
|
||||||
x="198"
|
|
||||||
y="14.999996"
|
|
||||||
width="3.9999928"
|
|
||||||
height="1.9999995"
|
|
||||||
id="rect4389" />
|
|
||||||
</g>
|
|
||||||
<rect
|
|
||||||
style="fill:#4c4c4c;fill-opacity:1;stroke:none"
|
|
||||||
id="rect3754-4"
|
|
||||||
width="16"
|
|
||||||
height="16"
|
|
||||||
x="76"
|
|
||||||
y="3.9999199" />
|
|
||||||
<path
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
d="m 85.10447,6.0157384 -0.0156,1.4063 c 3.02669,-0.2402 0.33008,3.6507996 2.48438,4.5780996 -2.18694,1.0938 0.49191,4.9069 -2.45313,4.5781 l -0.0156,1.4219 c 5.70828,0.559 1.03264,-5.1005 4.70313,-5.2656 l 0,-1.4063 c -3.61303,-0.027 1.11893,-5.7069996 -4.70313,-5.3124996 z"
|
|
||||||
id="path4351"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
sodipodi:nodetypes="cccccccc" />
|
|
||||||
<path
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
d="m 82.78125,5.9984384 0.0156,1.4063 c -3.02668,-0.2402 -0.33007,3.6506996 -2.48437,4.5780996 2.18694,1.0938 -0.49192,4.9069 2.45312,4.5781 l 0.0156,1.4219 c -5.70827,0.559 -1.03263,-5.1004 -4.70312,-5.2656 l 0,-1.4063 c 3.61303,-0.027 -1.11894,-5.7070996 4.70312,-5.3124996 z"
|
|
||||||
id="path4351-9"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
sodipodi:nodetypes="cccccccc" />
|
|
||||||
<rect
|
|
||||||
style="fill:#4c4c4c;fill-opacity:1;stroke:none"
|
|
||||||
id="rect3754-25"
|
|
||||||
width="16"
|
|
||||||
height="16"
|
|
||||||
x="100"
|
|
||||||
y="3.9999199" />
|
|
||||||
<path
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:none"
|
|
||||||
d="m 103.719,5.6719384 0,12.7187996 3.03125,0 0,-1.5313 -1.34375,0 0,-9.6249996 1.375,0 0,-1.5625 z"
|
|
||||||
id="path2987"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
<path
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:none"
|
|
||||||
d="m 112.2185,5.6721984 0,12.7187996 -3.03125,0 0,-1.5313 1.34375,0 0,-9.6249996 -1.375,0 0,-1.5625 z"
|
|
||||||
id="path2987-1"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
<rect
|
|
||||||
style="fill:#4c4c4c;fill-opacity:1;stroke:none"
|
|
||||||
id="rect3754-73"
|
|
||||||
width="16"
|
|
||||||
height="16"
|
|
||||||
x="124"
|
|
||||||
y="3.9999199" />
|
|
||||||
<path
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:none"
|
|
||||||
d="m 126.2824,17.602938 1.78957,0 1.14143,-2.8641 5.65364,0 1.14856,2.8641 1.76565,0 -4.78687,-11.1610996 -1.91903,0 z"
|
|
||||||
id="path3780"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
sodipodi:nodetypes="ccccccccc" />
|
|
||||||
<path
|
|
||||||
style="fill:#4c4c4c;fill-opacity:1;stroke:none"
|
|
||||||
d="m 129.72704,13.478838 4.60852,0.01 -2.30426,-5.5497996 z"
|
|
||||||
id="path3782"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
<rect
|
|
||||||
style="fill:#4c4c4c;fill-opacity:1;stroke:none"
|
|
||||||
id="rect3754-35"
|
|
||||||
width="16"
|
|
||||||
height="16"
|
|
||||||
x="148"
|
|
||||||
y="3.9999199" />
|
|
||||||
<path
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:none"
|
|
||||||
d="m 156.47655,5.8917384 0,2.1797 0.46093,2.3983996 1.82813,0 0.39844,-2.3983996 0,-2.1797 z"
|
|
||||||
id="path5008-2"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
sodipodi:nodetypes="ccccccc" />
|
|
||||||
<path
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:none"
|
|
||||||
d="m 152.51561,5.8906384 0,2.1797 0.46094,2.3983996 1.82812,0 0.39844,-2.3983996 0,-2.1797 z"
|
|
||||||
id="path5008-2-8"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
sodipodi:nodetypes="ccccccc" />
|
|
||||||
<rect
|
|
||||||
id="svg_1-7-2"
|
|
||||||
height="1.9999961"
|
|
||||||
width="11.999996"
|
|
||||||
y="64"
|
|
||||||
x="54"
|
|
||||||
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0" />
|
|
||||||
<rect
|
|
||||||
id="svg_1-7-2-2"
|
|
||||||
height="2.9999905"
|
|
||||||
width="2.9999907"
|
|
||||||
y="52"
|
|
||||||
x="80.000008"
|
|
||||||
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0" />
|
|
||||||
<rect
|
|
||||||
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
|
|
||||||
x="85.000008"
|
|
||||||
y="52"
|
|
||||||
width="2.9999907"
|
|
||||||
height="2.9999905"
|
|
||||||
id="rect4561" />
|
|
||||||
<rect
|
|
||||||
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
|
|
||||||
x="80.000008"
|
|
||||||
y="58"
|
|
||||||
width="2.9999907"
|
|
||||||
height="2.9999905"
|
|
||||||
id="rect4563" />
|
|
||||||
<rect
|
|
||||||
id="rect4565"
|
|
||||||
height="2.9999905"
|
|
||||||
width="2.9999907"
|
|
||||||
y="58"
|
|
||||||
x="85.000008"
|
|
||||||
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0" />
|
|
||||||
<rect
|
|
||||||
id="rect4567"
|
|
||||||
height="2.9999905"
|
|
||||||
width="2.9999907"
|
|
||||||
y="64"
|
|
||||||
x="80.000008"
|
|
||||||
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0" />
|
|
||||||
<rect
|
|
||||||
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
|
|
||||||
x="85.000008"
|
|
||||||
y="64"
|
|
||||||
width="2.9999907"
|
|
||||||
height="2.9999905"
|
|
||||||
id="rect4569" />
|
|
||||||
<circle
|
|
||||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#4c4c4c;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
|
|
||||||
id="path4571"
|
|
||||||
cx="110.06081"
|
|
||||||
cy="57.939209"
|
|
||||||
r="4.7438836" />
|
|
||||||
<rect
|
|
||||||
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
|
|
||||||
x="116.64566"
|
|
||||||
y="-31.79752"
|
|
||||||
width="4.229713"
|
|
||||||
height="6.4053884"
|
|
||||||
id="rect4563-2"
|
|
||||||
transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,0,0)" />
|
|
||||||
<path
|
|
||||||
style="fill:#4c4c4c;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
d="M 125,56 138.77027,56.095 132,64 Z"
|
|
||||||
id="path4613"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
sodipodi:nodetypes="cccc" />
|
|
||||||
<path
|
|
||||||
sodipodi:nodetypes="cccc"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
id="path4615"
|
|
||||||
d="M 149,64 162.77027,63.905 156,56 Z"
|
|
||||||
style="fill:#4c4c4c;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
|
||||||
<rect
|
|
||||||
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
|
|
||||||
x="54"
|
|
||||||
y="53"
|
|
||||||
width="11.999996"
|
|
||||||
height="1.9999961"
|
|
||||||
id="rect4638" />
|
|
||||||
<rect
|
|
||||||
id="svg_1-7-2-24"
|
|
||||||
height="1.9999957"
|
|
||||||
width="12.99999"
|
|
||||||
y="-56"
|
|
||||||
x="53"
|
|
||||||
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
|
|
||||||
transform="matrix(0,1,-1,0,0,0)" />
|
|
||||||
<rect
|
|
||||||
transform="matrix(0,1,-1,0,0,0)"
|
|
||||||
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
|
|
||||||
x="53"
|
|
||||||
y="-66"
|
|
||||||
width="12.99999"
|
|
||||||
height="1.9999957"
|
|
||||||
id="rect4657" />
|
|
||||||
<rect
|
|
||||||
id="rect4659"
|
|
||||||
height="0.99999291"
|
|
||||||
width="11.999999"
|
|
||||||
y="57"
|
|
||||||
x="54"
|
|
||||||
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0" />
|
|
||||||
<rect
|
|
||||||
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
|
|
||||||
x="54"
|
|
||||||
y="88.000122"
|
|
||||||
width="11.999996"
|
|
||||||
height="1.9999961"
|
|
||||||
id="rect4661" />
|
|
||||||
<rect
|
|
||||||
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
|
|
||||||
x="80.000008"
|
|
||||||
y="76.000122"
|
|
||||||
width="2.9999907"
|
|
||||||
height="2.9999905"
|
|
||||||
id="rect4663" />
|
|
||||||
<rect
|
|
||||||
id="rect4665"
|
|
||||||
height="2.9999905"
|
|
||||||
width="2.9999907"
|
|
||||||
y="76.000122"
|
|
||||||
x="85.000008"
|
|
||||||
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1" />
|
|
||||||
<rect
|
|
||||||
id="rect4667"
|
|
||||||
height="2.9999905"
|
|
||||||
width="2.9999907"
|
|
||||||
y="82.000122"
|
|
||||||
x="80.000008"
|
|
||||||
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1" />
|
|
||||||
<rect
|
|
||||||
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
|
|
||||||
x="85.000008"
|
|
||||||
y="82.000122"
|
|
||||||
width="2.9999907"
|
|
||||||
height="2.9999905"
|
|
||||||
id="rect4669" />
|
|
||||||
<rect
|
|
||||||
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
|
|
||||||
x="80.000008"
|
|
||||||
y="88.000122"
|
|
||||||
width="2.9999907"
|
|
||||||
height="2.9999905"
|
|
||||||
id="rect4671" />
|
|
||||||
<rect
|
|
||||||
id="rect4673"
|
|
||||||
height="2.9999905"
|
|
||||||
width="2.9999907"
|
|
||||||
y="88.000122"
|
|
||||||
x="85.000008"
|
|
||||||
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1" />
|
|
||||||
<circle
|
|
||||||
r="4.7438836"
|
|
||||||
cy="81.939331"
|
|
||||||
cx="110.06081"
|
|
||||||
id="circle4675"
|
|
||||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#d3d3d3;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
|
||||||
<rect
|
|
||||||
transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,0,0)"
|
|
||||||
id="rect4677"
|
|
||||||
height="6.4053884"
|
|
||||||
width="4.229713"
|
|
||||||
y="-14.826816"
|
|
||||||
x="133.6163"
|
|
||||||
style="fill:#d3d3d3;fill-opacity:1;stroke:#d3d3d3;stroke-width:0;stroke-opacity:1" />
|
|
||||||
<path
|
|
||||||
sodipodi:nodetypes="cccc"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
id="path4679"
|
|
||||||
d="m 125,80.000005 13.77027,0.09499 L 132,87.999992 Z"
|
|
||||||
style="fill:#d3d3d3;fill-opacity:1;fill-rule:evenodd;stroke:#d3d3d3;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
|
||||||
<path
|
|
||||||
style="fill:#d3d3d3;fill-opacity:1;fill-rule:evenodd;stroke:#d3d3d3;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
d="M 149,88.0002 162.77027,87.9052 156,80.0002 Z"
|
|
||||||
id="path4681"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
sodipodi:nodetypes="cccc" />
|
|
||||||
<rect
|
|
||||||
id="rect4683"
|
|
||||||
height="1.9999961"
|
|
||||||
width="11.999996"
|
|
||||||
y="77.000122"
|
|
||||||
x="54"
|
|
||||||
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1" />
|
|
||||||
<rect
|
|
||||||
transform="matrix(0,1,-1,0,0,0)"
|
|
||||||
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
|
|
||||||
x="77.000122"
|
|
||||||
y="-56"
|
|
||||||
width="12.99999"
|
|
||||||
height="1.9999957"
|
|
||||||
id="rect4685" />
|
|
||||||
<rect
|
|
||||||
id="rect4687"
|
|
||||||
height="1.9999957"
|
|
||||||
width="12.99999"
|
|
||||||
y="-66"
|
|
||||||
x="77.000122"
|
|
||||||
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
|
|
||||||
transform="matrix(0,1,-1,0,0,0)" />
|
|
||||||
<rect
|
|
||||||
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
|
|
||||||
x="54"
|
|
||||||
y="81.000122"
|
|
||||||
width="11.999999"
|
|
||||||
height="0.99999291"
|
|
||||||
id="rect4689" />
|
|
||||||
<rect
|
|
||||||
id="rect4761-1"
|
|
||||||
height="1.9999945"
|
|
||||||
width="15.99999"
|
|
||||||
y="101"
|
|
||||||
x="76.000008"
|
|
||||||
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
|
|
||||||
<rect
|
|
||||||
id="rect4761-0"
|
|
||||||
height="1.9999945"
|
|
||||||
width="15.99999"
|
|
||||||
y="105"
|
|
||||||
x="76.000008"
|
|
||||||
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
|
|
||||||
<rect
|
|
||||||
id="rect4761-7"
|
|
||||||
height="1.9999945"
|
|
||||||
width="9"
|
|
||||||
y="109"
|
|
||||||
x="76.000008"
|
|
||||||
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
|
|
||||||
<rect
|
|
||||||
id="rect4761-1-1"
|
|
||||||
height="1.9999945"
|
|
||||||
width="12"
|
|
||||||
y="125"
|
|
||||||
x="76.000008"
|
|
||||||
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
|
|
||||||
<rect
|
|
||||||
id="rect4761-1-1-4"
|
|
||||||
height="1.9999945"
|
|
||||||
width="10"
|
|
||||||
y="137"
|
|
||||||
x="76.000008"
|
|
||||||
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
|
|
||||||
<rect
|
|
||||||
id="rect4761-1-1-4-4"
|
|
||||||
height="1.9999945"
|
|
||||||
width="10"
|
|
||||||
y="129"
|
|
||||||
x="82"
|
|
||||||
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
|
|
||||||
<rect
|
|
||||||
id="rect4761-1-1-4-4-3"
|
|
||||||
height="1.9999945"
|
|
||||||
width="9"
|
|
||||||
y="133"
|
|
||||||
x="82"
|
|
||||||
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
|
|
||||||
<path
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.8;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.66157866;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
|
||||||
d="m 36.398438,100.0254 c -0.423362,-0.013 -0.846847,0.01 -1.265626,0.062 -1.656562,0.2196 -3.244567,0.9739 -4.507812,2.2266 L 29,100.5991 l -2.324219,7.7129 7.826172,-1.9062 -1.804687,-1.9063 c 1.597702,-1.5308 4.048706,-1.8453 5.984375,-0.7207 1.971162,1.1452 2.881954,3.3975 2.308593,5.5508 -0.573361,2.1533 -2.533865,3.6953 -4.830078,3.6953 l 0,3.0742 c 3.550756,0 6.710442,-2.4113 7.650391,-5.9414 0.939949,-3.5301 -0.618463,-7.2736 -3.710938,-9.0703 -1.159678,-0.6738 -2.431087,-1.0231 -3.701171,-1.0625 z"
|
|
||||||
id="path4138" />
|
|
||||||
<path
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.8;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.66157866;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
|
||||||
d="m 59.722656,99.9629 c -1.270084,0.039 -2.541493,0.3887 -3.701172,1.0625 -3.092475,1.7967 -4.650886,5.5402 -3.710937,9.0703 0.939949,3.5301 4.09768,5.9414 7.648437,5.9414 l 0,-3.0742 c -2.296214,0 -4.256717,-1.542 -4.830078,-3.6953 -0.573361,-2.1533 0.337432,-4.4056 2.308594,-5.5508 1.935731,-1.1246 4.38863,-0.8102 5.986326,0.7207 l -1.806638,1.9063 7.828128,1.9062 -2.32422,-7.7129 -1.62696,1.7168 c -1.26338,-1.2531 -2.848917,-2.0088 -4.505855,-2.2285 -0.418778,-0.055 -0.842263,-0.076 -1.265625,-0.062 z"
|
|
||||||
id="path4138-1" />
|
|
||||||
<path
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
style="opacity:0.8;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.96599996;stroke-miterlimit:4;stroke-dasharray:none"
|
|
||||||
d="m 10.5,100 0,2 -2.4999996,0 L 12,107 l 4,-5 -2.5,0 0,-2 -3,0 z"
|
|
||||||
id="path3055-0-77" />
|
|
||||||
<path
|
|
||||||
style="opacity:0.8;fill:none;stroke:#ffffff;stroke-width:1.96599996;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
d="m 4.9850574,108.015 14.0298856,-0.03"
|
|
||||||
id="path5244-5-0-5"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
sodipodi:nodetypes="cc" />
|
|
||||||
<path
|
|
||||||
style="opacity:0.8;fill:none;stroke:#ffffff;stroke-width:1.96599996;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
d="m 4.9849874,132.015 14.0298866,-0.03"
|
|
||||||
id="path5244-5-0-5-8"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
sodipodi:nodetypes="cc" />
|
|
||||||
<path
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.4;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.66157866;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
|
||||||
d="m 36.398438,123.9629 c -0.423362,-0.013 -0.846847,0.01 -1.265626,0.062 -1.656562,0.2196 -3.244567,0.9739 -4.507812,2.2266 L 29,124.5366 l -2.324219,7.7129 7.826172,-1.9062 -1.804687,-1.9063 c 1.597702,-1.5308 4.048706,-1.8453 5.984375,-0.7207 1.971162,1.1453 2.881954,3.3975 2.308593,5.5508 -0.573361,2.1533 -2.533864,3.6953 -4.830078,3.6953 l 0,3.0742 c 3.550757,0 6.710442,-2.4093 7.650391,-5.9394 0.939949,-3.5301 -0.618463,-7.2756 -3.710938,-9.0723 -1.159678,-0.6737 -2.431087,-1.0231 -3.701171,-1.0625 z"
|
|
||||||
id="path4138-12" />
|
|
||||||
<path
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.4;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.66157866;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
|
||||||
d="m 59.722656,123.9629 c -1.270084,0.039 -2.541493,0.3888 -3.701172,1.0625 -3.092475,1.7967 -4.650886,5.5422 -3.710937,9.0723 0.939949,3.5301 4.09768,5.9394 7.648437,5.9394 l 0,-3.0742 c -2.296214,0 -4.256717,-1.542 -4.830078,-3.6953 -0.573361,-2.1533 0.337432,-4.4055 2.308594,-5.5508 1.935731,-1.1246 4.38863,-0.8102 5.986326,0.7207 l -1.806638,1.9063 7.828128,1.9062 -2.32422,-7.7129 -1.62696,1.7168 c -1.26338,-1.2531 -2.848917,-2.0088 -4.505855,-2.2285 -0.418778,-0.055 -0.842263,-0.076 -1.265625,-0.062 z"
|
|
||||||
id="path4138-1-3" />
|
|
||||||
<path
|
|
||||||
id="path6191"
|
|
||||||
d="m 10.5,116 0,-2 -2.4999996,0 L 12,109 l 4,5 -2.5,0 0,2 -3,0 z"
|
|
||||||
style="opacity:0.8;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.96599996;stroke-miterlimit:4;stroke-dasharray:none"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
<path
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
style="opacity:0.8;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.96599996;stroke-miterlimit:4;stroke-dasharray:none"
|
|
||||||
d="m 10.5,129 0,-2 -2.4999996,0 L 12,122 l 4,5 -2.5,0 0,2 -3,0 z"
|
|
||||||
id="path6193" />
|
|
||||||
<path
|
|
||||||
id="path6195"
|
|
||||||
d="m 10.5,135 0,2 -2.4999996,0 L 12,142 l 4,-5 -2.5,0 0,-2 -3,0 z"
|
|
||||||
style="opacity:0.8;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.96599996;stroke-miterlimit:4;stroke-dasharray:none"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
<path
|
|
||||||
sodipodi:type="star"
|
|
||||||
style="fill:#4d4d4d;fill-opacity:0.90196078;stroke:#d3d3d3;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
|
|
||||||
id="path4500"
|
|
||||||
sodipodi:sides="3"
|
|
||||||
sodipodi:cx="11.55581"
|
|
||||||
sodipodi:cy="60.073242"
|
|
||||||
sodipodi:r1="5.1116104"
|
|
||||||
sodipodi:r2="2.5558052"
|
|
||||||
sodipodi:arg1="0"
|
|
||||||
sodipodi:arg2="1.0471976"
|
|
||||||
inkscape:flatsided="false"
|
|
||||||
inkscape:rounded="0"
|
|
||||||
inkscape:randomized="0"
|
|
||||||
d="m 16.66742,60.073242 -3.833708,2.213392 -3.8337072,2.213393 0,-4.426785 0,-4.426784 3.8337082,2.213392 z"
|
|
||||||
inkscape:transform-center-x="-1.2779026" />
|
|
||||||
<path
|
|
||||||
inkscape:transform-center-x="1.277902"
|
|
||||||
d="m -31.500004,60.073242 -3.833708,2.213392 -3.833707,2.213393 0,-4.426785 0,-4.426784 3.833707,2.213392 z"
|
|
||||||
inkscape:randomized="0"
|
|
||||||
inkscape:rounded="0"
|
|
||||||
inkscape:flatsided="false"
|
|
||||||
sodipodi:arg2="1.0471976"
|
|
||||||
sodipodi:arg1="0"
|
|
||||||
sodipodi:r2="2.5558052"
|
|
||||||
sodipodi:r1="5.1116104"
|
|
||||||
sodipodi:cy="60.073242"
|
|
||||||
sodipodi:cx="-36.611614"
|
|
||||||
sodipodi:sides="3"
|
|
||||||
id="path4502"
|
|
||||||
style="fill:#4d4d4d;fill-opacity:0.90196078;stroke:#d3d3d3;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
|
|
||||||
sodipodi:type="star"
|
|
||||||
transform="scale(-1,1)" />
|
|
||||||
<path
|
|
||||||
d="m 16.66742,60.073212 -3.833708,2.213392 -3.8337072,2.213392 0,-4.426784 0,-4.426785 3.8337082,2.213392 z"
|
|
||||||
inkscape:randomized="0"
|
|
||||||
inkscape:rounded="0"
|
|
||||||
inkscape:flatsided="false"
|
|
||||||
sodipodi:arg2="1.0471976"
|
|
||||||
sodipodi:arg1="0"
|
|
||||||
sodipodi:r2="2.5558052"
|
|
||||||
sodipodi:r1="5.1116104"
|
|
||||||
sodipodi:cy="60.073212"
|
|
||||||
sodipodi:cx="11.55581"
|
|
||||||
sodipodi:sides="3"
|
|
||||||
id="path4504"
|
|
||||||
style="fill:#4d4d4d;fill-opacity:0.90196078;stroke:#d3d3d3;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
|
|
||||||
sodipodi:type="star"
|
|
||||||
transform="matrix(0,1,-1,0,72.0074,71.7877)"
|
|
||||||
inkscape:transform-center-y="1.2779029" />
|
|
||||||
<path
|
|
||||||
inkscape:transform-center-y="-1.2779026"
|
|
||||||
transform="matrix(0,-1,-1,0,96,96)"
|
|
||||||
sodipodi:type="star"
|
|
||||||
style="fill:#4d4d4d;fill-opacity:0.90196078;stroke:#d3d3d3;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
|
|
||||||
id="path4506"
|
|
||||||
sodipodi:sides="3"
|
|
||||||
sodipodi:cx="11.55581"
|
|
||||||
sodipodi:cy="60.073212"
|
|
||||||
sodipodi:r1="5.1116104"
|
|
||||||
sodipodi:r2="2.5558052"
|
|
||||||
sodipodi:arg1="0"
|
|
||||||
sodipodi:arg2="1.0471976"
|
|
||||||
inkscape:flatsided="false"
|
|
||||||
inkscape:rounded="0"
|
|
||||||
inkscape:randomized="0"
|
|
||||||
d="m 16.66742,60.073212 -3.833708,2.213392 -3.8337072,2.213392 0,-4.426784 0,-4.426785 3.8337082,2.213392 z" />
|
|
||||||
<path
|
|
||||||
sodipodi:nodetypes="cccc"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
id="path4615-5"
|
|
||||||
d="m 171.82574,65.174193 16.34854,0 -8.17427,-13.348454 z"
|
|
||||||
style="fill:#fbb917;fill-opacity:1;fill-rule:evenodd;stroke:#fbb917;stroke-width:1.65161395;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
|
||||||
<path
|
|
||||||
style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
d="m 179,55 0,6 2,0 0,-6"
|
|
||||||
id="path4300"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
sodipodi:nodetypes="cccc" />
|
|
||||||
<path
|
|
||||||
style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
d="m 179,62 0,2 2,0 0,-2"
|
|
||||||
id="path4300-6"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
sodipodi:nodetypes="cccc" />
|
|
||||||
<path
|
|
||||||
style="fill:#ffffff;fill-opacity:0.8;fill-rule:evenodd;stroke:#ffffff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:0.8"
|
|
||||||
d="M 99.994369,113.0221 102,114.98353 l 7,-6.9558 3,0.97227 2,-1 1,-2 0,-3 -3,3 -3,-3 3,-3 -3,0 -2,1 -1,2 0.99437,3.0221 z"
|
|
||||||
id="path4268"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
sodipodi:nodetypes="ccccccccccccccc" />
|
|
||||||
<rect
|
|
||||||
id="rect4175-3-5"
|
|
||||||
height="16"
|
|
||||||
width="16"
|
|
||||||
y="4"
|
|
||||||
x="220"
|
|
||||||
style="fill:#4c4c4c;fill-opacity:1;stroke:none;stroke-width:0" />
|
|
||||||
<path
|
|
||||||
style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
d="m 234,6 0,2 -5,5 0,5 -2,0 0,-5 -5,-5 0,-2"
|
|
||||||
id="path3546"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
sodipodi:nodetypes="cccccccc" />
|
|
||||||
<g
|
|
||||||
transform="matrix(1.3333328,0,0,-1.5999992,-139.9999,127.19999)"
|
|
||||||
id="g4383-6">
|
|
||||||
<rect
|
|
||||||
id="rect4385-2"
|
|
||||||
height="1.2499905"
|
|
||||||
width="5.9999924"
|
|
||||||
y="12.625005"
|
|
||||||
x="198.00002"
|
|
||||||
style="fill:#ffffff;fill-opacity:0.8;stroke:#000000;stroke-width:0" />
|
|
||||||
<rect
|
|
||||||
style="fill:#ffffff;fill-opacity:0.8;stroke:#000000;stroke-width:0"
|
|
||||||
x="198.00002"
|
|
||||||
y="15.125007"
|
|
||||||
width="7.4999928"
|
|
||||||
height="1.2499949"
|
|
||||||
id="rect4387-9" />
|
|
||||||
<rect
|
|
||||||
style="fill:#ffffff;fill-opacity:0.8;stroke:#000000;stroke-width:0"
|
|
||||||
x="198.00002"
|
|
||||||
y="7.6250024"
|
|
||||||
width="2.9999909"
|
|
||||||
height="1.2499905"
|
|
||||||
id="rect4389-1-0" />
|
|
||||||
<rect
|
|
||||||
style="fill:#ffffff;fill-opacity:0.8;stroke:#000000;stroke-width:0"
|
|
||||||
x="198.00002"
|
|
||||||
y="10.125004"
|
|
||||||
width="4.4999919"
|
|
||||||
height="1.2499905"
|
|
||||||
id="rect4389-1-9" />
|
|
||||||
<path
|
|
||||||
style="fill:#ffffff;fill-opacity:0.8;fill-rule:evenodd;stroke:none;stroke-width:0.68465352px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
d="m 207.00001,16.375004 0,-5.625005 -2.25,0 3,-3.1250014 3,3.1250014 -2.25,0 0,5.625005 -1.5,0"
|
|
||||||
id="path4402"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
sodipodi:nodetypes="cccccccc" />
|
|
||||||
</g>
|
|
||||||
<path
|
|
||||||
style="fill:#ffffff;fill-opacity:0.8;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
d="m 164,100 0,3 -6,6 0,7 -4,0 0,-7 -6,-6 0,-3"
|
|
||||||
id="path3546-2-2"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
sodipodi:nodetypes="cccccccc" />
|
|
||||||
<rect
|
|
||||||
style="fill:#4c4c4c;fill-opacity:1;stroke:none;stroke-width:0"
|
|
||||||
id="svg_1-3"
|
|
||||||
height="16"
|
|
||||||
width="16"
|
|
||||||
y="28"
|
|
||||||
x="4" />
|
|
||||||
<path
|
|
||||||
sodipodi:nodetypes="ccccccccc"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
id="path4402-5-7"
|
|
||||||
d="m 15,41 0,-7 -4,0 0,3 -5,-4 5,-4 0,3 6,0 0,9"
|
|
||||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.68465352px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 31 KiB |
@ -4,45 +4,14 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<!-- <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">-->
|
<!-- <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">-->
|
||||||
<title>Three js demo for particle physics</title>
|
<title>Three js demo for particle physics</title>
|
||||||
<script type="text/javascript" src="main.bundle.js"></script>
|
<link rel="stylesheet" href="css/bootstrap.min.css">
|
||||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
|
|
||||||
<link rel="stylesheet" href="css/jsoneditor.min.css">
|
|
||||||
<link rel="stylesheet" href="css/main.css">
|
<link rel="stylesheet" href="css/main.css">
|
||||||
|
<link rel="stylesheet" href="css/fileDrop.css">
|
||||||
|
<script type="text/javascript" src="main.bundle.js"></script>
|
||||||
|
<script type="text/javascript" src ="js/jquery-3.4.1.min.js"></script>
|
||||||
|
<script type="text/javascript" src ="js/bootstrap.bundle.min.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body class="testApp">
|
<body class="application">
|
||||||
|
<div class="container-fluid" id = "app"> </div>
|
||||||
<div class="container">
|
</body>
|
||||||
<h1>GDML demo</h1>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="container-fluid h-100">
|
|
||||||
<div class="row h-100">
|
|
||||||
<div class="col-lg-3">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-body">
|
|
||||||
<a>Load file:</a>
|
|
||||||
<input type="file" id="file_load_button" accept=".gdml, application/xml, application/json"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-body" id="drop_zone">
|
|
||||||
Load data
|
|
||||||
<br/>
|
|
||||||
(drag file here)
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="overflow-auto" id="tree"></div>
|
|
||||||
</div>
|
|
||||||
<div class="col-lg-6">
|
|
||||||
<div id="canvas"></div>
|
|
||||||
</div>
|
|
||||||
<div class="col-lg-3">
|
|
||||||
<div class="row" id="config"></div>
|
|
||||||
<div class="row" id="editor"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
|
|
||||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script></body>
|
|
||||||
</html>
|
</html>
|
@ -1,20 +0,0 @@
|
|||||||
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 hep.dataforge.vis.spatial.stringify
|
|
||||||
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 = visual.stringify()
|
|
||||||
File("D:\\Work\\Projects\\gdml.kt\\gdml-source\\cubes.json").writeText(json)
|
|
||||||
}
|
|
@ -1,13 +1,13 @@
|
|||||||
package hep.dataforge.vis.spatial.gdml.demo
|
package hep.dataforge.vision.gdml.demo
|
||||||
|
|
||||||
import hep.dataforge.context.Global
|
import hep.dataforge.context.Global
|
||||||
import hep.dataforge.vis.editor.VisualObjectEditorFragment
|
import hep.dataforge.vision.editor.VisualObjectEditorFragment
|
||||||
import hep.dataforge.vis.editor.VisualObjectTreeFragment
|
import hep.dataforge.vision.editor.VisualObjectTreeFragment
|
||||||
import hep.dataforge.vis.spatial.Material3D
|
import hep.dataforge.vision.gdml.toVision
|
||||||
import hep.dataforge.vis.spatial.Visual3D
|
import hep.dataforge.vision.solid.SolidManager
|
||||||
import hep.dataforge.vis.spatial.VisualGroup3D
|
import hep.dataforge.vision.solid.SolidMaterial
|
||||||
import hep.dataforge.vis.spatial.fx.FX3DPlugin
|
import hep.dataforge.vision.solid.fx.FX3DPlugin
|
||||||
import hep.dataforge.vis.spatial.fx.FXCanvas3D
|
import hep.dataforge.vision.solid.fx.FXCanvas3D
|
||||||
import javafx.geometry.Orientation
|
import javafx.geometry.Orientation
|
||||||
import javafx.scene.Parent
|
import javafx.scene.Parent
|
||||||
import javafx.stage.FileChooser
|
import javafx.stage.FileChooser
|
||||||
@ -24,9 +24,9 @@ class GDMLView : View() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val propertyEditor = VisualObjectEditorFragment {
|
private val propertyEditor = VisualObjectEditorFragment {
|
||||||
it.allProperties()
|
it.getAllProperties()
|
||||||
}.apply {
|
}.apply {
|
||||||
descriptorProperty.set(Material3D.descriptor)
|
descriptorProperty.set(SolidMaterial.descriptor)
|
||||||
itemProperty.bind(treeFragment.selectedProperty)
|
itemProperty.bind(treeFragment.selectedProperty)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,10 +36,15 @@ class GDMLView : View() {
|
|||||||
buttonbar {
|
buttonbar {
|
||||||
button("Load GDML/json") {
|
button("Load GDML/json") {
|
||||||
action {
|
action {
|
||||||
|
runAsync {
|
||||||
val file = chooseFile("Select a GDML/json file", filters = fileNameFilter).firstOrNull()
|
val file = chooseFile("Select a GDML/json file", filters = fileNameFilter).firstOrNull()
|
||||||
?: return@action
|
?: return@runAsync null
|
||||||
val visual: VisualGroup3D = Visual3D.readFile(file)
|
SolidManager.readFile(file)
|
||||||
canvas.render(visual)
|
} ui {
|
||||||
|
if (it != null) {
|
||||||
|
canvas.render(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -51,6 +56,14 @@ class GDMLView : View() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
runAsync {
|
||||||
|
cubes().toVision()
|
||||||
|
} ui {
|
||||||
|
canvas.render(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val fileNameFilter = arrayOf(
|
private val fileNameFilter = arrayOf(
|
||||||
FileChooser.ExtensionFilter("GDML", "*.gdml", "*.xml"),
|
FileChooser.ExtensionFilter("GDML", "*.gdml", "*.xml"),
|
@ -1,51 +1,54 @@
|
|||||||
package hep.dataforge.vis.spatial.gdml.demo
|
package hep.dataforge.vision.gdml.demo
|
||||||
|
|
||||||
import hep.dataforge.meta.setProperty
|
import hep.dataforge.meta.DFExperimental
|
||||||
|
import hep.dataforge.meta.setItem
|
||||||
import hep.dataforge.values.asValue
|
import hep.dataforge.values.asValue
|
||||||
import hep.dataforge.vis.spatial.Material3D
|
import hep.dataforge.vision.gdml.LUnit
|
||||||
import hep.dataforge.vis.spatial.Visual3D
|
import hep.dataforge.vision.gdml.readFile
|
||||||
import hep.dataforge.vis.spatial.VisualGroup3D
|
import hep.dataforge.vision.gdml.toVision
|
||||||
import hep.dataforge.vis.spatial.gdml.LUnit
|
import hep.dataforge.vision.solid.SolidGroup
|
||||||
import hep.dataforge.vis.spatial.gdml.readFile
|
import hep.dataforge.vision.solid.SolidManager
|
||||||
import hep.dataforge.vis.spatial.gdml.toVisual
|
import hep.dataforge.vision.solid.SolidMaterial
|
||||||
import scientifik.gdml.GDML
|
import scientifik.gdml.GDML
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.zip.GZIPInputStream
|
import java.util.zip.GZIPInputStream
|
||||||
import java.util.zip.ZipInputStream
|
import java.util.zip.ZipInputStream
|
||||||
|
|
||||||
fun Visual3D.Companion.readFile(file: File): VisualGroup3D = when {
|
@OptIn(DFExperimental::class)
|
||||||
|
fun SolidManager.Companion.readFile(file: File): SolidGroup = when {
|
||||||
file.extension == "gdml" || file.extension == "xml" -> {
|
file.extension == "gdml" || file.extension == "xml" -> {
|
||||||
GDML.readFile(file.toPath()).toVisual {
|
GDML.readFile(file.toPath()).toVision {
|
||||||
lUnit = LUnit.CM
|
lUnit = LUnit.CM
|
||||||
|
|
||||||
solidConfiguration = { parent, solid ->
|
solidConfiguration = { parent, solid ->
|
||||||
if (solid.name == "cave") {
|
if (solid.name == "cave") {
|
||||||
setProperty(Material3D.MATERIAL_WIREFRAME_KEY, true.asValue())
|
setItem(SolidMaterial.MATERIAL_WIREFRAME_KEY, true.asValue())
|
||||||
}
|
}
|
||||||
if (parent.physVolumes.isNotEmpty()) {
|
if (parent.physVolumes.isNotEmpty()) {
|
||||||
useStyle("opaque") {
|
useStyle("opaque") {
|
||||||
Material3D.MATERIAL_OPACITY_KEY put 0.3
|
SolidMaterial.MATERIAL_OPACITY_KEY put 0.3
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file.extension == "json" -> VisualGroup3D.parseJson(file.readText())
|
file.extension == "json" -> SolidGroup.parseJson(file.readText())
|
||||||
file.name.endsWith("json.zip") -> {
|
file.name.endsWith("json.zip") -> {
|
||||||
file.inputStream().use {
|
file.inputStream().use {
|
||||||
val unzip = ZipInputStream(it, Charsets.UTF_8)
|
val unzip = ZipInputStream(it, Charsets.UTF_8)
|
||||||
val text = unzip.readBytes().decodeToString()
|
val text = unzip.readBytes().decodeToString()
|
||||||
VisualGroup3D.parseJson(text)
|
SolidGroup.parseJson(text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file.name.endsWith("json.gz") -> {
|
file.name.endsWith("json.gz") -> {
|
||||||
file.inputStream().use {
|
file.inputStream().use {
|
||||||
val unzip = GZIPInputStream(it)
|
val unzip = GZIPInputStream(it)
|
||||||
val text = unzip.readBytes().decodeToString()
|
val text = unzip.readBytes().decodeToString()
|
||||||
VisualGroup3D.parseJson(text)
|
SolidGroup.parseJson(text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> error("Unknown extension ${file.extension}")
|
else -> error("Unknown extension ${file.extension}")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Visual3D.Companion.readFile(fileName: String): VisualGroup3D = readFile(File(fileName))
|
@OptIn(DFExperimental::class)
|
||||||
|
fun SolidManager.Companion.readFile(fileName: String): SolidGroup = readFile(File(fileName))
|
@ -0,0 +1,28 @@
|
|||||||
|
package hep.dataforge.vision.gdml.demo
|
||||||
|
|
||||||
|
import hep.dataforge.vision.gdml.LUnit
|
||||||
|
import hep.dataforge.vision.gdml.readFile
|
||||||
|
import hep.dataforge.vision.gdml.toVision
|
||||||
|
import hep.dataforge.vision.solid.stringify
|
||||||
|
import scientifik.gdml.GDML
|
||||||
|
import java.io.File
|
||||||
|
import java.nio.file.Paths
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
require(args.isNotEmpty()){"At least one argument is required"}
|
||||||
|
val inputFileName = args[0]
|
||||||
|
require(inputFileName.endsWith(".gdml")){"GDML required"}
|
||||||
|
val outputFileName = args.getOrNull(1)?:inputFileName.replace(".gdml",".json")
|
||||||
|
|
||||||
|
val gdml = GDML.readFile(Paths.get(inputFileName))
|
||||||
|
//GDML.readFile(Paths.get("D:\\Work\\Projects\\visionforge\\visionforge-spatial-gdml\\src\\jvmTest\\resources\\gdml\\simple1.gdml"))
|
||||||
|
|
||||||
|
val visual = gdml.toVision {
|
||||||
|
lUnit = LUnit.CM
|
||||||
|
}
|
||||||
|
|
||||||
|
val json = visual.stringify()
|
||||||
|
println(json)
|
||||||
|
File(outputFileName).writeText(json)
|
||||||
|
//File("D:\\Work\\Projects\\gdml.kt\\gdml-source\\cubes.json").writeText(json)
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package hep.dataforge.vis.spatial
|
package hep.dataforge.vision.solid
|
||||||
|
|
||||||
import hep.dataforge.names.asName
|
import hep.dataforge.names.asName
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
@ -9,7 +9,7 @@ class FileSerializationTest {
|
|||||||
@Ignore
|
@Ignore
|
||||||
fun testFileRead(){
|
fun testFileRead(){
|
||||||
val text = this::class.java.getResourceAsStream("/cubes.json").readBytes().decodeToString()
|
val text = this::class.java.getResourceAsStream("/cubes.json").readBytes().decodeToString()
|
||||||
val visual = VisualGroup3D.parseJson(text)
|
val visual = SolidGroup.parseJson(text)
|
||||||
visual["composite_001".asName()]
|
visual["composite_001".asName()]
|
||||||
}
|
}
|
||||||
}
|
}
|
34
demo/muon-monitor/README.md
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
|
||||||
|
### Muon Monitor Visualization
|
||||||
|
|
||||||
|
This directory contains a full-stack application example built with `visionforge`.
|
||||||
|
It is visualizing the
|
||||||
|
[Muon Monitor](http://npm.mipt.ru/projects/physics.html#mounMonitor) experiment set-up,
|
||||||
|
including experiment's geometry and events (particle tracks).
|
||||||
|
|
||||||
|
#### Reusing code and going Full-Stack with Kotlin Multiplatform
|
||||||
|
|
||||||
|
The application includes both server back-end generating events, as well as client
|
||||||
|
visualization front-end.
|
||||||
|
|
||||||
|
As is common for Kotlin multiplatform projects, the code base of this simple application
|
||||||
|
is put in the following main directories:
|
||||||
|
* `commonMain` - common code, used by both JS client and JVM server. For example, the `Monitor`
|
||||||
|
object describes general geometry definitions needed in all parts of the application.
|
||||||
|
* `jsMain` - JavaScript client code. It performs visualization and reads events from the server.
|
||||||
|
* `jvmMain` - JVM server code. It runs `ktor` HTTP server, responding with event data when
|
||||||
|
client requests them.
|
||||||
|
|
||||||
|
Note that in a more traditional approach when client and server are developed separately
|
||||||
|
and possibly using different languages, there would be no common code and benefits associated
|
||||||
|
with it.
|
||||||
|
|
||||||
|
##### Building project
|
||||||
|
|
||||||
|
To run full-stack Muon Monitor Visualization application (both JVM server and Web browser front-end),
|
||||||
|
run `demo/muon-monitor/application/run` task.
|
||||||
|
|
||||||
|
##### Example view:
|
||||||
|
|
||||||
|
![](../../doc/resources/muon-monitor.png)
|
||||||
|
|
@ -1,3 +1,4 @@
|
|||||||
|
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.Companion.MAIN_COMPILATION_NAME
|
||||||
import scientifik.jsDistDirectory
|
import scientifik.jsDistDirectory
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
@ -20,25 +21,29 @@ kotlin {
|
|||||||
keep("ktor-ktor-io.\$\$importsForInline\$\$.ktor-ktor-io.io.ktor.utils.io")
|
keep("ktor-ktor-io.\$\$importsForInline\$\$.ktor-ktor-io.io.ktor.utils.io")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
webpackTask {
|
||||||
|
mode = org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig.Mode.PRODUCTION
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
jvm {
|
jvm {
|
||||||
withJava()
|
withJava()
|
||||||
compilations.findByName("main").apply {
|
compilations[MAIN_COMPILATION_NAME]?.apply {
|
||||||
tasks.getByName<ProcessResources>("jvmProcessResources") {
|
tasks.getByName<ProcessResources>(processResourcesTaskName) {
|
||||||
dependsOn(installJS)
|
dependsOn(installJS)
|
||||||
afterEvaluate {
|
afterEvaluate {
|
||||||
from(project.jsDistDirectory)
|
from(project.jsDistDirectory)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
commonMain {
|
commonMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":dataforge-vis-spatial"))
|
implementation(project(":visionforge-solid"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
jvmMain {
|
jvmMain {
|
||||||
@ -50,6 +55,7 @@ kotlin {
|
|||||||
}
|
}
|
||||||
jsMain {
|
jsMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
|
implementation(project(":ui:bootstrap"))
|
||||||
implementation("io.ktor:ktor-client-js:$ktorVersion")
|
implementation("io.ktor:ktor-client-js:$ktorVersion")
|
||||||
implementation("io.ktor:ktor-client-serialization-js:$ktorVersion")
|
implementation("io.ktor:ktor-client-serialization-js:$ktorVersion")
|
||||||
implementation(npm("text-encoding"))
|
implementation(npm("text-encoding"))
|
||||||
@ -69,6 +75,13 @@ application {
|
|||||||
mainClassName = "ru.mipt.npm.muon.monitor.server.MMServerKt"
|
mainClassName = "ru.mipt.npm.muon.monitor.server.MMServerKt"
|
||||||
}
|
}
|
||||||
|
|
||||||
//configure<JavaFXOptions> {
|
distributions {
|
||||||
// modules("javafx.controls")
|
main {
|
||||||
//}
|
contents {
|
||||||
|
from("$buildDir/libs") {
|
||||||
|
rename("${rootProject.name}-jvm", rootProject.name)
|
||||||
|
into("lib")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,8 @@
|
|||||||
@file:UseSerializers(Point3DSerializer::class)
|
@file:UseSerializers(Point3DSerializer::class)
|
||||||
package ru.mipt.npm.muon.monitor
|
package ru.mipt.npm.muon.monitor
|
||||||
|
|
||||||
import hep.dataforge.vis.spatial.Point3D
|
import hep.dataforge.vision.solid.Point3D
|
||||||
import hep.dataforge.vis.spatial.Point3DSerializer
|
import hep.dataforge.vision.solid.Point3DSerializer
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.UseSerializers
|
import kotlinx.serialization.UseSerializers
|
||||||
|
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
package ru.mipt.npm.muon.monitor
|
package ru.mipt.npm.muon.monitor
|
||||||
|
|
||||||
import hep.dataforge.vis.removeAll
|
import hep.dataforge.vision.removeAll
|
||||||
import hep.dataforge.vis.spatial.*
|
import hep.dataforge.vision.solid.*
|
||||||
import ru.mipt.npm.muon.monitor.Monitor.CENTRAL_LAYER_Z
|
import ru.mipt.npm.muon.monitor.Monitor.CENTRAL_LAYER_Z
|
||||||
import ru.mipt.npm.muon.monitor.Monitor.LOWER_LAYER_Z
|
import ru.mipt.npm.muon.monitor.Monitor.LOWER_LAYER_Z
|
||||||
import ru.mipt.npm.muon.monitor.Monitor.UPPER_LAYER_Z
|
import ru.mipt.npm.muon.monitor.Monitor.UPPER_LAYER_Z
|
||||||
import kotlin.math.PI
|
import kotlin.math.PI
|
||||||
|
|
||||||
class Model {
|
class Model {
|
||||||
private val map = HashMap<String, VisualGroup3D>()
|
private val map = HashMap<String, SolidGroup>()
|
||||||
private val events = HashSet<Event>()
|
private val events = HashSet<Event>()
|
||||||
|
|
||||||
private fun VisualGroup3D.pixel(pixel: SC1) {
|
private fun SolidGroup.pixel(pixel: SC1) {
|
||||||
val group = group(pixel.name) {
|
val group = group(pixel.name) {
|
||||||
position = Point3D(pixel.center.x, pixel.center.y, pixel.center.z)
|
position = Point3D(pixel.center.x, pixel.center.y, pixel.center.z)
|
||||||
box(pixel.xSize, pixel.ySize, pixel.zSize)
|
box(pixel.xSize, pixel.ySize, pixel.zSize)
|
||||||
@ -23,7 +23,7 @@ class Model {
|
|||||||
map[pixel.name] = group
|
map[pixel.name] = group
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun VisualGroup3D.detector(detector: SC16) {
|
private fun SolidGroup.detector(detector: SC16) {
|
||||||
group(detector.name) {
|
group(detector.name) {
|
||||||
detector.pixels.forEach {
|
detector.pixels.forEach {
|
||||||
pixel(it)
|
pixel(it)
|
||||||
@ -31,9 +31,9 @@ class Model {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var tracks: VisualGroup3D
|
var tracks: SolidGroup
|
||||||
|
|
||||||
val root: VisualGroup3D = VisualGroup3D().apply {
|
val root: SolidGroup = SolidGroup().apply {
|
||||||
rotationX = PI / 2
|
rotationX = PI / 2
|
||||||
group("bottom") {
|
group("bottom") {
|
||||||
Monitor.detectors.filter { it.center.z == LOWER_LAYER_Z }.forEach {
|
Monitor.detectors.filter { it.center.z == LOWER_LAYER_Z }.forEach {
|
||||||
@ -62,7 +62,8 @@ class Model {
|
|||||||
|
|
||||||
fun reset() {
|
fun reset() {
|
||||||
map.values.forEach {
|
map.values.forEach {
|
||||||
it.setProperty(Material3D.MATERIAL_COLOR_KEY, null)
|
it.config
|
||||||
|
it.setItem(SolidMaterial.MATERIAL_COLOR_KEY, null)
|
||||||
}
|
}
|
||||||
tracks.removeAll()
|
tracks.removeAll()
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
package ru.mipt.npm.muon.monitor
|
package ru.mipt.npm.muon.monitor
|
||||||
|
|
||||||
import hep.dataforge.vis.spatial.Point3D
|
import hep.dataforge.vision.solid.Point3D
|
||||||
import hep.dataforge.vis.spatial.plus
|
import hep.dataforge.vision.solid.plus
|
||||||
import ru.mipt.npm.muon.monitor.Monitor.PIXEL_XY_SIZE
|
import ru.mipt.npm.muon.monitor.Monitor.PIXEL_XY_SIZE
|
||||||
import ru.mipt.npm.muon.monitor.Monitor.PIXEL_Z_SIZE
|
import ru.mipt.npm.muon.monitor.Monitor.PIXEL_Z_SIZE
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A single pixel
|
* A single pixel
|
||||||
*/
|
*/
|
||||||
open class SC1(
|
class SC1(
|
||||||
val name: String,
|
val name: String,
|
||||||
val center: Point3D,
|
val center: Point3D,
|
||||||
val xSize: Double = PIXEL_XY_SIZE, val ySize: Double = PIXEL_XY_SIZE, val zSize: Double = PIXEL_Z_SIZE
|
val xSize: Double = PIXEL_XY_SIZE, val ySize: Double = PIXEL_XY_SIZE, val zSize: Double = PIXEL_Z_SIZE
|
||||||
|
@ -0,0 +1,166 @@
|
|||||||
|
package ru.mipt.npm.muon.monitor
|
||||||
|
|
||||||
|
import hep.dataforge.context.Context
|
||||||
|
import hep.dataforge.names.Name
|
||||||
|
import hep.dataforge.names.NameToken
|
||||||
|
import hep.dataforge.names.isEmpty
|
||||||
|
import hep.dataforge.vision.Vision
|
||||||
|
import hep.dataforge.vision.bootstrap.card
|
||||||
|
import hep.dataforge.vision.bootstrap.objectTree
|
||||||
|
import hep.dataforge.vision.react.component
|
||||||
|
import hep.dataforge.vision.react.configEditor
|
||||||
|
import hep.dataforge.vision.react.state
|
||||||
|
import hep.dataforge.vision.solid.specifications.Camera
|
||||||
|
import hep.dataforge.vision.solid.specifications.Canvas3DOptions
|
||||||
|
import hep.dataforge.vision.solid.three.ThreeCanvas
|
||||||
|
import hep.dataforge.vision.solid.three.ThreeCanvasComponent
|
||||||
|
import hep.dataforge.vision.solid.three.canvasControls
|
||||||
|
import io.ktor.client.HttpClient
|
||||||
|
import io.ktor.client.request.get
|
||||||
|
import kotlinx.coroutines.GlobalScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.html.js.onClickFunction
|
||||||
|
import react.RProps
|
||||||
|
import react.dom.*
|
||||||
|
import kotlin.math.PI
|
||||||
|
|
||||||
|
interface MMAppProps : RProps {
|
||||||
|
var model: Model
|
||||||
|
var context: Context
|
||||||
|
var connection: HttpClient
|
||||||
|
var selected: Name?
|
||||||
|
}
|
||||||
|
|
||||||
|
private val canvasConfig = Canvas3DOptions {
|
||||||
|
camera = Camera {
|
||||||
|
distance = 2100.0
|
||||||
|
latitude = PI / 6
|
||||||
|
azimuth = PI + PI / 6
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val MMApp = component<MMAppProps> { props ->
|
||||||
|
var selected by state { props.selected }
|
||||||
|
var canvas: ThreeCanvas? by state { null }
|
||||||
|
|
||||||
|
val select: (Name?) -> Unit = {
|
||||||
|
selected = it
|
||||||
|
}
|
||||||
|
|
||||||
|
val visual = props.model.root
|
||||||
|
|
||||||
|
div("row") {
|
||||||
|
h1("mx-auto") {
|
||||||
|
+"Muon monitor demo"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
div("row") {
|
||||||
|
div("col-lg-3 px-0 overflow-auto") {
|
||||||
|
//tree
|
||||||
|
card("Object tree") {
|
||||||
|
objectTree(visual, selected, select)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
div("col-lg-6") {
|
||||||
|
//canvas
|
||||||
|
child(ThreeCanvasComponent::class) {
|
||||||
|
attrs {
|
||||||
|
this.context = props.context
|
||||||
|
this.obj = visual
|
||||||
|
this.options = canvasConfig
|
||||||
|
this.selected = selected
|
||||||
|
this.clickCallback = select
|
||||||
|
this.canvasCallback = {
|
||||||
|
canvas = it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
div("col-lg-3") {
|
||||||
|
div("row") {
|
||||||
|
//settings
|
||||||
|
canvas?.let {
|
||||||
|
card("Canvas configuration") {
|
||||||
|
canvasControls(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
card("Events") {
|
||||||
|
button {
|
||||||
|
+"Next"
|
||||||
|
attrs {
|
||||||
|
onClickFunction = {
|
||||||
|
GlobalScope.launch {
|
||||||
|
val event = props.connection.get<Event>("http://localhost:8080/event")
|
||||||
|
props.model.displayEvent(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
+"Clear"
|
||||||
|
attrs {
|
||||||
|
onClickFunction = {
|
||||||
|
props.model.reset()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
div("row") {
|
||||||
|
div("container-fluid p-0") {
|
||||||
|
nav {
|
||||||
|
attrs {
|
||||||
|
attributes["aria-label"] = "breadcrumb"
|
||||||
|
}
|
||||||
|
ol("breadcrumb") {
|
||||||
|
li("breadcrumb-item") {
|
||||||
|
button(classes = "btn btn-link p-0") {
|
||||||
|
+"World"
|
||||||
|
attrs {
|
||||||
|
onClickFunction = {
|
||||||
|
selected = hep.dataforge.names.Name.EMPTY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (selected != null) {
|
||||||
|
val tokens = ArrayList<NameToken>(selected?.length ?: 1)
|
||||||
|
selected?.tokens?.forEach { token ->
|
||||||
|
tokens.add(token)
|
||||||
|
val fullName = Name(tokens.toList())
|
||||||
|
li("breadcrumb-item") {
|
||||||
|
button(classes = "btn btn-link p-0") {
|
||||||
|
+token.toString()
|
||||||
|
attrs {
|
||||||
|
onClickFunction = {
|
||||||
|
console.log("Selected = $fullName")
|
||||||
|
selected = fullName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
div("row") {
|
||||||
|
//properties
|
||||||
|
card("Properties") {
|
||||||
|
selected.let { selected ->
|
||||||
|
val selectedObject: Vision? = when {
|
||||||
|
selected == null -> null
|
||||||
|
selected.isEmpty() -> visual
|
||||||
|
else -> visual[selected]
|
||||||
|
}
|
||||||
|
if (selectedObject != null) {
|
||||||
|
configEditor(selectedObject, default = selectedObject.getAllProperties(), key = selected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -3,31 +3,15 @@ package ru.mipt.npm.muon.monitor
|
|||||||
import hep.dataforge.context.Global
|
import hep.dataforge.context.Global
|
||||||
import hep.dataforge.js.Application
|
import hep.dataforge.js.Application
|
||||||
import hep.dataforge.js.startApplication
|
import hep.dataforge.js.startApplication
|
||||||
import hep.dataforge.names.Name
|
import hep.dataforge.vision.solid.SolidManager
|
||||||
import hep.dataforge.names.isEmpty
|
|
||||||
import hep.dataforge.vis.VisualGroup
|
|
||||||
import hep.dataforge.vis.VisualObject
|
|
||||||
import hep.dataforge.vis.editor.card
|
|
||||||
import hep.dataforge.vis.editor.objectTree
|
|
||||||
import hep.dataforge.vis.editor.visualPropertyEditor
|
|
||||||
import hep.dataforge.vis.spatial.Visual3D
|
|
||||||
import hep.dataforge.vis.spatial.VisualObject3D
|
|
||||||
import hep.dataforge.vis.spatial.three.ThreePlugin
|
|
||||||
import hep.dataforge.vis.spatial.three.displayCanvasControls
|
|
||||||
import hep.dataforge.vis.spatial.three.output
|
|
||||||
import info.laht.threekt.math.Vector3
|
|
||||||
import io.ktor.client.HttpClient
|
import io.ktor.client.HttpClient
|
||||||
import io.ktor.client.features.json.JsonFeature
|
import io.ktor.client.features.json.JsonFeature
|
||||||
import io.ktor.client.features.json.serializer.KotlinxSerializer
|
import io.ktor.client.features.json.serializer.KotlinxSerializer
|
||||||
import io.ktor.client.request.get
|
|
||||||
import kotlinx.coroutines.GlobalScope
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.html.js.button
|
|
||||||
import kotlinx.html.js.onClickFunction
|
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import org.w3c.dom.HTMLElement
|
import react.child
|
||||||
|
import react.dom.div
|
||||||
|
import react.dom.render
|
||||||
import kotlin.browser.document
|
import kotlin.browser.document
|
||||||
import kotlin.dom.clear
|
|
||||||
|
|
||||||
private class MMDemoApp : Application {
|
private class MMDemoApp : Application {
|
||||||
|
|
||||||
@ -35,71 +19,28 @@ private class MMDemoApp : Application {
|
|||||||
|
|
||||||
private val connection = HttpClient {
|
private val connection = HttpClient {
|
||||||
install(JsonFeature) {
|
install(JsonFeature) {
|
||||||
serializer = KotlinxSerializer(Json(context = Visual3D.serialModule))
|
serializer = KotlinxSerializer(Json(context = SolidManager.serialModule))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO introduce react application
|
||||||
|
|
||||||
override fun start(state: Map<String, Any>) {
|
override fun start(state: Map<String, Any>) {
|
||||||
|
|
||||||
val context = Global.context("demo") {}
|
val context = Global.context("demo") {}
|
||||||
val three = context.plugins.load(ThreePlugin)
|
val element = document.getElementById("app") ?: error("Element with id 'app' not found on page")
|
||||||
//val url = URL("https://drive.google.com/open?id=1w5e7fILMN83JGgB8WANJUYm8OW2s0WVO")
|
|
||||||
|
|
||||||
val canvasElement = document.getElementById("canvas") ?: error("Element with id 'canvas' not found on page")
|
render(element) {
|
||||||
val settingsElement = document.getElementById("settings")
|
div("container-fluid h-100") {
|
||||||
?: error("Element with id 'settings' not found on page")
|
child(MMApp) {
|
||||||
val treeElement = document.getElementById("tree") ?: error("Element with id 'tree' not found on page")
|
attrs {
|
||||||
val editorElement = document.getElementById("editor") ?: error("Element with id 'editor' not found on page")
|
model = this@MMDemoApp.model
|
||||||
|
connection = this@MMDemoApp.connection
|
||||||
canvasElement.clear()
|
this.context = context
|
||||||
val visual: VisualObject3D = model.root
|
|
||||||
|
|
||||||
//output.camera.layers.enable(1)
|
|
||||||
val canvas = three.output(canvasElement as HTMLElement)
|
|
||||||
|
|
||||||
canvas.camera.layers.set(0)
|
|
||||||
canvas.camera.position.z = -2000.0
|
|
||||||
canvas.camera.position.y = 500.0
|
|
||||||
canvas.camera.lookAt(Vector3(0, 0, 0))
|
|
||||||
settingsElement.displayCanvasControls(canvas) {
|
|
||||||
card("Events") {
|
|
||||||
button {
|
|
||||||
+"Next"
|
|
||||||
onClickFunction = {
|
|
||||||
GlobalScope.launch {
|
|
||||||
val event = connection.get<Event>("http://localhost:8080/event")
|
|
||||||
model.displayEvent(event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
button {
|
|
||||||
+"Clear"
|
|
||||||
onClickFunction = {
|
|
||||||
model.reset()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun selectElement(name: Name) {
|
|
||||||
val child: VisualObject = when {
|
|
||||||
name.isEmpty() -> visual
|
|
||||||
visual is VisualGroup -> visual[name] ?: return
|
|
||||||
else -> return
|
|
||||||
}
|
|
||||||
editorElement.visualPropertyEditor(name, child, descriptor = VisualObject3D.descriptor)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// canvas.clickListener = ::selectElement
|
|
||||||
|
|
||||||
//tree.visualObjectTree(visual, editor::propertyEditor)
|
|
||||||
treeElement.objectTree(visual) { name ->
|
|
||||||
selectElement(name)
|
|
||||||
canvas.highlight(name)
|
|
||||||
}
|
|
||||||
canvas.render(visual)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,748 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
width="240"
|
|
||||||
height="144"
|
|
||||||
id="svg4136"
|
|
||||||
version="1.1"
|
|
||||||
inkscape:version="0.91 r13725"
|
|
||||||
sodipodi:docname="jsoneditor-icons.svg">
|
|
||||||
<title
|
|
||||||
id="title6512">JSON Editor Icons</title>
|
|
||||||
<metadata
|
|
||||||
id="metadata4148">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title>JSON Editor Icons</dc:title>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<defs
|
|
||||||
id="defs4146" />
|
|
||||||
<sodipodi:namedview
|
|
||||||
pagecolor="#ff63ff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1"
|
|
||||||
objecttolerance="10"
|
|
||||||
gridtolerance="10"
|
|
||||||
guidetolerance="10"
|
|
||||||
inkscape:pageopacity="0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:window-width="1920"
|
|
||||||
inkscape:window-height="1026"
|
|
||||||
id="namedview4144"
|
|
||||||
showgrid="true"
|
|
||||||
inkscape:zoom="4"
|
|
||||||
inkscape:cx="13.229181"
|
|
||||||
inkscape:cy="119.82429"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="0"
|
|
||||||
inkscape:window-maximized="1"
|
|
||||||
inkscape:current-layer="svg4136"
|
|
||||||
showguides="false"
|
|
||||||
borderlayer="false"
|
|
||||||
inkscape:showpageshadow="true"
|
|
||||||
showborder="true">
|
|
||||||
<inkscape:grid
|
|
||||||
type="xygrid"
|
|
||||||
id="grid4640"
|
|
||||||
empspacing="24" />
|
|
||||||
</sodipodi:namedview>
|
|
||||||
<!-- Created with SVG-edit - http://svg-edit.googlecode.com/ -->
|
|
||||||
<rect
|
|
||||||
style="fill:#4c4c4c;fill-opacity:1;stroke:none;stroke-width:0"
|
|
||||||
id="svg_1"
|
|
||||||
height="16"
|
|
||||||
width="16"
|
|
||||||
y="4"
|
|
||||||
x="4" />
|
|
||||||
<rect
|
|
||||||
id="svg_1-7"
|
|
||||||
height="16"
|
|
||||||
width="16"
|
|
||||||
y="3.999995"
|
|
||||||
x="28.000006"
|
|
||||||
style="fill:#ec3f29;fill-opacity:0.94117647;stroke:none;stroke-width:0" />
|
|
||||||
<rect
|
|
||||||
style="fill:#4c4c4c;fill-opacity:1;stroke:none;stroke-width:0"
|
|
||||||
x="52.000004"
|
|
||||||
y="3.999995"
|
|
||||||
width="16"
|
|
||||||
height="16"
|
|
||||||
id="rect4165" />
|
|
||||||
<rect
|
|
||||||
id="rect4175"
|
|
||||||
height="16"
|
|
||||||
width="16"
|
|
||||||
y="3.9999852"
|
|
||||||
x="172.00002"
|
|
||||||
style="fill:#4c4c4c;fill-opacity:1;stroke:none;stroke-width:0" />
|
|
||||||
<rect
|
|
||||||
id="rect4175-3"
|
|
||||||
height="16"
|
|
||||||
width="16"
|
|
||||||
y="3.999995"
|
|
||||||
x="196"
|
|
||||||
style="fill:#4c4c4c;fill-opacity:1;stroke:none;stroke-width:0" />
|
|
||||||
<g
|
|
||||||
id="g4299"
|
|
||||||
style="stroke:none">
|
|
||||||
<rect
|
|
||||||
x="7.0000048"
|
|
||||||
y="10.999998"
|
|
||||||
width="9.9999924"
|
|
||||||
height="1.9999986"
|
|
||||||
id="svg_1-1"
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0" />
|
|
||||||
<rect
|
|
||||||
x="11.000005"
|
|
||||||
y="7.0000114"
|
|
||||||
width="1.9999955"
|
|
||||||
height="9.9999838"
|
|
||||||
id="svg_1-1-1"
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0" />
|
|
||||||
</g>
|
|
||||||
<g
|
|
||||||
id="g4299-3"
|
|
||||||
transform="matrix(0.70710678,-0.70710678,0.70710678,0.70710678,19.029435,12.000001)"
|
|
||||||
style="stroke:none">
|
|
||||||
<rect
|
|
||||||
x="7.0000048"
|
|
||||||
y="10.999998"
|
|
||||||
width="9.9999924"
|
|
||||||
height="1.9999986"
|
|
||||||
id="svg_1-1-0"
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0" />
|
|
||||||
<rect
|
|
||||||
x="11.000005"
|
|
||||||
y="7.0000114"
|
|
||||||
width="1.9999955"
|
|
||||||
height="9.9999838"
|
|
||||||
id="svg_1-1-1-9"
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0" />
|
|
||||||
</g>
|
|
||||||
<rect
|
|
||||||
id="svg_1-7-5"
|
|
||||||
height="6.9999905"
|
|
||||||
width="6.9999909"
|
|
||||||
y="7.0000048"
|
|
||||||
x="55.000004"
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0" />
|
|
||||||
<rect
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:#4c4c4c;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
x="58"
|
|
||||||
y="10.00001"
|
|
||||||
width="6.9999909"
|
|
||||||
height="6.9999905"
|
|
||||||
id="rect4354" />
|
|
||||||
<rect
|
|
||||||
id="svg_1-7-5-7"
|
|
||||||
height="6.9999905"
|
|
||||||
width="6.9999909"
|
|
||||||
y="10.000005"
|
|
||||||
x="58.000004"
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:#3c80df;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.94117647" />
|
|
||||||
<g
|
|
||||||
id="g4378">
|
|
||||||
<rect
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0"
|
|
||||||
x="198"
|
|
||||||
y="10.999999"
|
|
||||||
width="7.9999909"
|
|
||||||
height="1.9999965"
|
|
||||||
id="svg_1-7-5-3" />
|
|
||||||
<rect
|
|
||||||
id="rect4374"
|
|
||||||
height="1.9999946"
|
|
||||||
width="11.999995"
|
|
||||||
y="7.0000005"
|
|
||||||
x="198"
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0" />
|
|
||||||
<rect
|
|
||||||
id="rect4376"
|
|
||||||
height="1.9999995"
|
|
||||||
width="3.9999928"
|
|
||||||
y="14.999996"
|
|
||||||
x="198"
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0" />
|
|
||||||
</g>
|
|
||||||
<g
|
|
||||||
transform="matrix(1,0,0,-1,-23.999995,23.999995)"
|
|
||||||
id="g4383">
|
|
||||||
<rect
|
|
||||||
id="rect4385"
|
|
||||||
height="1.9999965"
|
|
||||||
width="7.9999909"
|
|
||||||
y="10.999999"
|
|
||||||
x="198"
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0" />
|
|
||||||
<rect
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0"
|
|
||||||
x="198"
|
|
||||||
y="7.0000005"
|
|
||||||
width="11.999995"
|
|
||||||
height="1.9999946"
|
|
||||||
id="rect4387" />
|
|
||||||
<rect
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0"
|
|
||||||
x="198"
|
|
||||||
y="14.999996"
|
|
||||||
width="3.9999928"
|
|
||||||
height="1.9999995"
|
|
||||||
id="rect4389" />
|
|
||||||
</g>
|
|
||||||
<rect
|
|
||||||
style="fill:#4c4c4c;fill-opacity:1;stroke:none"
|
|
||||||
id="rect3754-4"
|
|
||||||
width="16"
|
|
||||||
height="16"
|
|
||||||
x="76"
|
|
||||||
y="3.9999199" />
|
|
||||||
<path
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
d="m 85.10447,6.0157384 -0.0156,1.4063 c 3.02669,-0.2402 0.33008,3.6507996 2.48438,4.5780996 -2.18694,1.0938 0.49191,4.9069 -2.45313,4.5781 l -0.0156,1.4219 c 5.70828,0.559 1.03264,-5.1005 4.70313,-5.2656 l 0,-1.4063 c -3.61303,-0.027 1.11893,-5.7069996 -4.70313,-5.3124996 z"
|
|
||||||
id="path4351"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
sodipodi:nodetypes="cccccccc" />
|
|
||||||
<path
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
d="m 82.78125,5.9984384 0.0156,1.4063 c -3.02668,-0.2402 -0.33007,3.6506996 -2.48437,4.5780996 2.18694,1.0938 -0.49192,4.9069 2.45312,4.5781 l 0.0156,1.4219 c -5.70827,0.559 -1.03263,-5.1004 -4.70312,-5.2656 l 0,-1.4063 c 3.61303,-0.027 -1.11894,-5.7070996 4.70312,-5.3124996 z"
|
|
||||||
id="path4351-9"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
sodipodi:nodetypes="cccccccc" />
|
|
||||||
<rect
|
|
||||||
style="fill:#4c4c4c;fill-opacity:1;stroke:none"
|
|
||||||
id="rect3754-25"
|
|
||||||
width="16"
|
|
||||||
height="16"
|
|
||||||
x="100"
|
|
||||||
y="3.9999199" />
|
|
||||||
<path
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:none"
|
|
||||||
d="m 103.719,5.6719384 0,12.7187996 3.03125,0 0,-1.5313 -1.34375,0 0,-9.6249996 1.375,0 0,-1.5625 z"
|
|
||||||
id="path2987"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
<path
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:none"
|
|
||||||
d="m 112.2185,5.6721984 0,12.7187996 -3.03125,0 0,-1.5313 1.34375,0 0,-9.6249996 -1.375,0 0,-1.5625 z"
|
|
||||||
id="path2987-1"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
<rect
|
|
||||||
style="fill:#4c4c4c;fill-opacity:1;stroke:none"
|
|
||||||
id="rect3754-73"
|
|
||||||
width="16"
|
|
||||||
height="16"
|
|
||||||
x="124"
|
|
||||||
y="3.9999199" />
|
|
||||||
<path
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:none"
|
|
||||||
d="m 126.2824,17.602938 1.78957,0 1.14143,-2.8641 5.65364,0 1.14856,2.8641 1.76565,0 -4.78687,-11.1610996 -1.91903,0 z"
|
|
||||||
id="path3780"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
sodipodi:nodetypes="ccccccccc" />
|
|
||||||
<path
|
|
||||||
style="fill:#4c4c4c;fill-opacity:1;stroke:none"
|
|
||||||
d="m 129.72704,13.478838 4.60852,0.01 -2.30426,-5.5497996 z"
|
|
||||||
id="path3782"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
<rect
|
|
||||||
style="fill:#4c4c4c;fill-opacity:1;stroke:none"
|
|
||||||
id="rect3754-35"
|
|
||||||
width="16"
|
|
||||||
height="16"
|
|
||||||
x="148"
|
|
||||||
y="3.9999199" />
|
|
||||||
<path
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:none"
|
|
||||||
d="m 156.47655,5.8917384 0,2.1797 0.46093,2.3983996 1.82813,0 0.39844,-2.3983996 0,-2.1797 z"
|
|
||||||
id="path5008-2"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
sodipodi:nodetypes="ccccccc" />
|
|
||||||
<path
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:none"
|
|
||||||
d="m 152.51561,5.8906384 0,2.1797 0.46094,2.3983996 1.82812,0 0.39844,-2.3983996 0,-2.1797 z"
|
|
||||||
id="path5008-2-8"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
sodipodi:nodetypes="ccccccc" />
|
|
||||||
<rect
|
|
||||||
id="svg_1-7-2"
|
|
||||||
height="1.9999961"
|
|
||||||
width="11.999996"
|
|
||||||
y="64"
|
|
||||||
x="54"
|
|
||||||
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0" />
|
|
||||||
<rect
|
|
||||||
id="svg_1-7-2-2"
|
|
||||||
height="2.9999905"
|
|
||||||
width="2.9999907"
|
|
||||||
y="52"
|
|
||||||
x="80.000008"
|
|
||||||
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0" />
|
|
||||||
<rect
|
|
||||||
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
|
|
||||||
x="85.000008"
|
|
||||||
y="52"
|
|
||||||
width="2.9999907"
|
|
||||||
height="2.9999905"
|
|
||||||
id="rect4561" />
|
|
||||||
<rect
|
|
||||||
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
|
|
||||||
x="80.000008"
|
|
||||||
y="58"
|
|
||||||
width="2.9999907"
|
|
||||||
height="2.9999905"
|
|
||||||
id="rect4563" />
|
|
||||||
<rect
|
|
||||||
id="rect4565"
|
|
||||||
height="2.9999905"
|
|
||||||
width="2.9999907"
|
|
||||||
y="58"
|
|
||||||
x="85.000008"
|
|
||||||
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0" />
|
|
||||||
<rect
|
|
||||||
id="rect4567"
|
|
||||||
height="2.9999905"
|
|
||||||
width="2.9999907"
|
|
||||||
y="64"
|
|
||||||
x="80.000008"
|
|
||||||
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0" />
|
|
||||||
<rect
|
|
||||||
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
|
|
||||||
x="85.000008"
|
|
||||||
y="64"
|
|
||||||
width="2.9999907"
|
|
||||||
height="2.9999905"
|
|
||||||
id="rect4569" />
|
|
||||||
<circle
|
|
||||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#4c4c4c;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
|
|
||||||
id="path4571"
|
|
||||||
cx="110.06081"
|
|
||||||
cy="57.939209"
|
|
||||||
r="4.7438836" />
|
|
||||||
<rect
|
|
||||||
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
|
|
||||||
x="116.64566"
|
|
||||||
y="-31.79752"
|
|
||||||
width="4.229713"
|
|
||||||
height="6.4053884"
|
|
||||||
id="rect4563-2"
|
|
||||||
transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,0,0)" />
|
|
||||||
<path
|
|
||||||
style="fill:#4c4c4c;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
d="M 125,56 138.77027,56.095 132,64 Z"
|
|
||||||
id="path4613"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
sodipodi:nodetypes="cccc" />
|
|
||||||
<path
|
|
||||||
sodipodi:nodetypes="cccc"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
id="path4615"
|
|
||||||
d="M 149,64 162.77027,63.905 156,56 Z"
|
|
||||||
style="fill:#4c4c4c;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
|
||||||
<rect
|
|
||||||
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
|
|
||||||
x="54"
|
|
||||||
y="53"
|
|
||||||
width="11.999996"
|
|
||||||
height="1.9999961"
|
|
||||||
id="rect4638" />
|
|
||||||
<rect
|
|
||||||
id="svg_1-7-2-24"
|
|
||||||
height="1.9999957"
|
|
||||||
width="12.99999"
|
|
||||||
y="-56"
|
|
||||||
x="53"
|
|
||||||
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
|
|
||||||
transform="matrix(0,1,-1,0,0,0)" />
|
|
||||||
<rect
|
|
||||||
transform="matrix(0,1,-1,0,0,0)"
|
|
||||||
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
|
|
||||||
x="53"
|
|
||||||
y="-66"
|
|
||||||
width="12.99999"
|
|
||||||
height="1.9999957"
|
|
||||||
id="rect4657" />
|
|
||||||
<rect
|
|
||||||
id="rect4659"
|
|
||||||
height="0.99999291"
|
|
||||||
width="11.999999"
|
|
||||||
y="57"
|
|
||||||
x="54"
|
|
||||||
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0" />
|
|
||||||
<rect
|
|
||||||
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
|
|
||||||
x="54"
|
|
||||||
y="88.000122"
|
|
||||||
width="11.999996"
|
|
||||||
height="1.9999961"
|
|
||||||
id="rect4661" />
|
|
||||||
<rect
|
|
||||||
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
|
|
||||||
x="80.000008"
|
|
||||||
y="76.000122"
|
|
||||||
width="2.9999907"
|
|
||||||
height="2.9999905"
|
|
||||||
id="rect4663" />
|
|
||||||
<rect
|
|
||||||
id="rect4665"
|
|
||||||
height="2.9999905"
|
|
||||||
width="2.9999907"
|
|
||||||
y="76.000122"
|
|
||||||
x="85.000008"
|
|
||||||
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1" />
|
|
||||||
<rect
|
|
||||||
id="rect4667"
|
|
||||||
height="2.9999905"
|
|
||||||
width="2.9999907"
|
|
||||||
y="82.000122"
|
|
||||||
x="80.000008"
|
|
||||||
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1" />
|
|
||||||
<rect
|
|
||||||
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
|
|
||||||
x="85.000008"
|
|
||||||
y="82.000122"
|
|
||||||
width="2.9999907"
|
|
||||||
height="2.9999905"
|
|
||||||
id="rect4669" />
|
|
||||||
<rect
|
|
||||||
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
|
|
||||||
x="80.000008"
|
|
||||||
y="88.000122"
|
|
||||||
width="2.9999907"
|
|
||||||
height="2.9999905"
|
|
||||||
id="rect4671" />
|
|
||||||
<rect
|
|
||||||
id="rect4673"
|
|
||||||
height="2.9999905"
|
|
||||||
width="2.9999907"
|
|
||||||
y="88.000122"
|
|
||||||
x="85.000008"
|
|
||||||
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1" />
|
|
||||||
<circle
|
|
||||||
r="4.7438836"
|
|
||||||
cy="81.939331"
|
|
||||||
cx="110.06081"
|
|
||||||
id="circle4675"
|
|
||||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#d3d3d3;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
|
||||||
<rect
|
|
||||||
transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,0,0)"
|
|
||||||
id="rect4677"
|
|
||||||
height="6.4053884"
|
|
||||||
width="4.229713"
|
|
||||||
y="-14.826816"
|
|
||||||
x="133.6163"
|
|
||||||
style="fill:#d3d3d3;fill-opacity:1;stroke:#d3d3d3;stroke-width:0;stroke-opacity:1" />
|
|
||||||
<path
|
|
||||||
sodipodi:nodetypes="cccc"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
id="path4679"
|
|
||||||
d="m 125,80.000005 13.77027,0.09499 L 132,87.999992 Z"
|
|
||||||
style="fill:#d3d3d3;fill-opacity:1;fill-rule:evenodd;stroke:#d3d3d3;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
|
||||||
<path
|
|
||||||
style="fill:#d3d3d3;fill-opacity:1;fill-rule:evenodd;stroke:#d3d3d3;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
d="M 149,88.0002 162.77027,87.9052 156,80.0002 Z"
|
|
||||||
id="path4681"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
sodipodi:nodetypes="cccc" />
|
|
||||||
<rect
|
|
||||||
id="rect4683"
|
|
||||||
height="1.9999961"
|
|
||||||
width="11.999996"
|
|
||||||
y="77.000122"
|
|
||||||
x="54"
|
|
||||||
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1" />
|
|
||||||
<rect
|
|
||||||
transform="matrix(0,1,-1,0,0,0)"
|
|
||||||
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
|
|
||||||
x="77.000122"
|
|
||||||
y="-56"
|
|
||||||
width="12.99999"
|
|
||||||
height="1.9999957"
|
|
||||||
id="rect4685" />
|
|
||||||
<rect
|
|
||||||
id="rect4687"
|
|
||||||
height="1.9999957"
|
|
||||||
width="12.99999"
|
|
||||||
y="-66"
|
|
||||||
x="77.000122"
|
|
||||||
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
|
|
||||||
transform="matrix(0,1,-1,0,0,0)" />
|
|
||||||
<rect
|
|
||||||
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
|
|
||||||
x="54"
|
|
||||||
y="81.000122"
|
|
||||||
width="11.999999"
|
|
||||||
height="0.99999291"
|
|
||||||
id="rect4689" />
|
|
||||||
<rect
|
|
||||||
id="rect4761-1"
|
|
||||||
height="1.9999945"
|
|
||||||
width="15.99999"
|
|
||||||
y="101"
|
|
||||||
x="76.000008"
|
|
||||||
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
|
|
||||||
<rect
|
|
||||||
id="rect4761-0"
|
|
||||||
height="1.9999945"
|
|
||||||
width="15.99999"
|
|
||||||
y="105"
|
|
||||||
x="76.000008"
|
|
||||||
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
|
|
||||||
<rect
|
|
||||||
id="rect4761-7"
|
|
||||||
height="1.9999945"
|
|
||||||
width="9"
|
|
||||||
y="109"
|
|
||||||
x="76.000008"
|
|
||||||
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
|
|
||||||
<rect
|
|
||||||
id="rect4761-1-1"
|
|
||||||
height="1.9999945"
|
|
||||||
width="12"
|
|
||||||
y="125"
|
|
||||||
x="76.000008"
|
|
||||||
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
|
|
||||||
<rect
|
|
||||||
id="rect4761-1-1-4"
|
|
||||||
height="1.9999945"
|
|
||||||
width="10"
|
|
||||||
y="137"
|
|
||||||
x="76.000008"
|
|
||||||
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
|
|
||||||
<rect
|
|
||||||
id="rect4761-1-1-4-4"
|
|
||||||
height="1.9999945"
|
|
||||||
width="10"
|
|
||||||
y="129"
|
|
||||||
x="82"
|
|
||||||
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
|
|
||||||
<rect
|
|
||||||
id="rect4761-1-1-4-4-3"
|
|
||||||
height="1.9999945"
|
|
||||||
width="9"
|
|
||||||
y="133"
|
|
||||||
x="82"
|
|
||||||
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
|
|
||||||
<path
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.8;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.66157866;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
|
||||||
d="m 36.398438,100.0254 c -0.423362,-0.013 -0.846847,0.01 -1.265626,0.062 -1.656562,0.2196 -3.244567,0.9739 -4.507812,2.2266 L 29,100.5991 l -2.324219,7.7129 7.826172,-1.9062 -1.804687,-1.9063 c 1.597702,-1.5308 4.048706,-1.8453 5.984375,-0.7207 1.971162,1.1452 2.881954,3.3975 2.308593,5.5508 -0.573361,2.1533 -2.533865,3.6953 -4.830078,3.6953 l 0,3.0742 c 3.550756,0 6.710442,-2.4113 7.650391,-5.9414 0.939949,-3.5301 -0.618463,-7.2736 -3.710938,-9.0703 -1.159678,-0.6738 -2.431087,-1.0231 -3.701171,-1.0625 z"
|
|
||||||
id="path4138" />
|
|
||||||
<path
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.8;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.66157866;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
|
||||||
d="m 59.722656,99.9629 c -1.270084,0.039 -2.541493,0.3887 -3.701172,1.0625 -3.092475,1.7967 -4.650886,5.5402 -3.710937,9.0703 0.939949,3.5301 4.09768,5.9414 7.648437,5.9414 l 0,-3.0742 c -2.296214,0 -4.256717,-1.542 -4.830078,-3.6953 -0.573361,-2.1533 0.337432,-4.4056 2.308594,-5.5508 1.935731,-1.1246 4.38863,-0.8102 5.986326,0.7207 l -1.806638,1.9063 7.828128,1.9062 -2.32422,-7.7129 -1.62696,1.7168 c -1.26338,-1.2531 -2.848917,-2.0088 -4.505855,-2.2285 -0.418778,-0.055 -0.842263,-0.076 -1.265625,-0.062 z"
|
|
||||||
id="path4138-1" />
|
|
||||||
<path
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
style="opacity:0.8;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.96599996;stroke-miterlimit:4;stroke-dasharray:none"
|
|
||||||
d="m 10.5,100 0,2 -2.4999996,0 L 12,107 l 4,-5 -2.5,0 0,-2 -3,0 z"
|
|
||||||
id="path3055-0-77" />
|
|
||||||
<path
|
|
||||||
style="opacity:0.8;fill:none;stroke:#ffffff;stroke-width:1.96599996;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
d="m 4.9850574,108.015 14.0298856,-0.03"
|
|
||||||
id="path5244-5-0-5"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
sodipodi:nodetypes="cc" />
|
|
||||||
<path
|
|
||||||
style="opacity:0.8;fill:none;stroke:#ffffff;stroke-width:1.96599996;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
d="m 4.9849874,132.015 14.0298866,-0.03"
|
|
||||||
id="path5244-5-0-5-8"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
sodipodi:nodetypes="cc" />
|
|
||||||
<path
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.4;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.66157866;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
|
||||||
d="m 36.398438,123.9629 c -0.423362,-0.013 -0.846847,0.01 -1.265626,0.062 -1.656562,0.2196 -3.244567,0.9739 -4.507812,2.2266 L 29,124.5366 l -2.324219,7.7129 7.826172,-1.9062 -1.804687,-1.9063 c 1.597702,-1.5308 4.048706,-1.8453 5.984375,-0.7207 1.971162,1.1453 2.881954,3.3975 2.308593,5.5508 -0.573361,2.1533 -2.533864,3.6953 -4.830078,3.6953 l 0,3.0742 c 3.550757,0 6.710442,-2.4093 7.650391,-5.9394 0.939949,-3.5301 -0.618463,-7.2756 -3.710938,-9.0723 -1.159678,-0.6737 -2.431087,-1.0231 -3.701171,-1.0625 z"
|
|
||||||
id="path4138-12" />
|
|
||||||
<path
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.4;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.66157866;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
|
||||||
d="m 59.722656,123.9629 c -1.270084,0.039 -2.541493,0.3888 -3.701172,1.0625 -3.092475,1.7967 -4.650886,5.5422 -3.710937,9.0723 0.939949,3.5301 4.09768,5.9394 7.648437,5.9394 l 0,-3.0742 c -2.296214,0 -4.256717,-1.542 -4.830078,-3.6953 -0.573361,-2.1533 0.337432,-4.4055 2.308594,-5.5508 1.935731,-1.1246 4.38863,-0.8102 5.986326,0.7207 l -1.806638,1.9063 7.828128,1.9062 -2.32422,-7.7129 -1.62696,1.7168 c -1.26338,-1.2531 -2.848917,-2.0088 -4.505855,-2.2285 -0.418778,-0.055 -0.842263,-0.076 -1.265625,-0.062 z"
|
|
||||||
id="path4138-1-3" />
|
|
||||||
<path
|
|
||||||
id="path6191"
|
|
||||||
d="m 10.5,116 0,-2 -2.4999996,0 L 12,109 l 4,5 -2.5,0 0,2 -3,0 z"
|
|
||||||
style="opacity:0.8;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.96599996;stroke-miterlimit:4;stroke-dasharray:none"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
<path
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
style="opacity:0.8;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.96599996;stroke-miterlimit:4;stroke-dasharray:none"
|
|
||||||
d="m 10.5,129 0,-2 -2.4999996,0 L 12,122 l 4,5 -2.5,0 0,2 -3,0 z"
|
|
||||||
id="path6193" />
|
|
||||||
<path
|
|
||||||
id="path6195"
|
|
||||||
d="m 10.5,135 0,2 -2.4999996,0 L 12,142 l 4,-5 -2.5,0 0,-2 -3,0 z"
|
|
||||||
style="opacity:0.8;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.96599996;stroke-miterlimit:4;stroke-dasharray:none"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
<path
|
|
||||||
sodipodi:type="star"
|
|
||||||
style="fill:#4d4d4d;fill-opacity:0.90196078;stroke:#d3d3d3;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
|
|
||||||
id="path4500"
|
|
||||||
sodipodi:sides="3"
|
|
||||||
sodipodi:cx="11.55581"
|
|
||||||
sodipodi:cy="60.073242"
|
|
||||||
sodipodi:r1="5.1116104"
|
|
||||||
sodipodi:r2="2.5558052"
|
|
||||||
sodipodi:arg1="0"
|
|
||||||
sodipodi:arg2="1.0471976"
|
|
||||||
inkscape:flatsided="false"
|
|
||||||
inkscape:rounded="0"
|
|
||||||
inkscape:randomized="0"
|
|
||||||
d="m 16.66742,60.073242 -3.833708,2.213392 -3.8337072,2.213393 0,-4.426785 0,-4.426784 3.8337082,2.213392 z"
|
|
||||||
inkscape:transform-center-x="-1.2779026" />
|
|
||||||
<path
|
|
||||||
inkscape:transform-center-x="1.277902"
|
|
||||||
d="m -31.500004,60.073242 -3.833708,2.213392 -3.833707,2.213393 0,-4.426785 0,-4.426784 3.833707,2.213392 z"
|
|
||||||
inkscape:randomized="0"
|
|
||||||
inkscape:rounded="0"
|
|
||||||
inkscape:flatsided="false"
|
|
||||||
sodipodi:arg2="1.0471976"
|
|
||||||
sodipodi:arg1="0"
|
|
||||||
sodipodi:r2="2.5558052"
|
|
||||||
sodipodi:r1="5.1116104"
|
|
||||||
sodipodi:cy="60.073242"
|
|
||||||
sodipodi:cx="-36.611614"
|
|
||||||
sodipodi:sides="3"
|
|
||||||
id="path4502"
|
|
||||||
style="fill:#4d4d4d;fill-opacity:0.90196078;stroke:#d3d3d3;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
|
|
||||||
sodipodi:type="star"
|
|
||||||
transform="scale(-1,1)" />
|
|
||||||
<path
|
|
||||||
d="m 16.66742,60.073212 -3.833708,2.213392 -3.8337072,2.213392 0,-4.426784 0,-4.426785 3.8337082,2.213392 z"
|
|
||||||
inkscape:randomized="0"
|
|
||||||
inkscape:rounded="0"
|
|
||||||
inkscape:flatsided="false"
|
|
||||||
sodipodi:arg2="1.0471976"
|
|
||||||
sodipodi:arg1="0"
|
|
||||||
sodipodi:r2="2.5558052"
|
|
||||||
sodipodi:r1="5.1116104"
|
|
||||||
sodipodi:cy="60.073212"
|
|
||||||
sodipodi:cx="11.55581"
|
|
||||||
sodipodi:sides="3"
|
|
||||||
id="path4504"
|
|
||||||
style="fill:#4d4d4d;fill-opacity:0.90196078;stroke:#d3d3d3;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
|
|
||||||
sodipodi:type="star"
|
|
||||||
transform="matrix(0,1,-1,0,72.0074,71.7877)"
|
|
||||||
inkscape:transform-center-y="1.2779029" />
|
|
||||||
<path
|
|
||||||
inkscape:transform-center-y="-1.2779026"
|
|
||||||
transform="matrix(0,-1,-1,0,96,96)"
|
|
||||||
sodipodi:type="star"
|
|
||||||
style="fill:#4d4d4d;fill-opacity:0.90196078;stroke:#d3d3d3;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
|
|
||||||
id="path4506"
|
|
||||||
sodipodi:sides="3"
|
|
||||||
sodipodi:cx="11.55581"
|
|
||||||
sodipodi:cy="60.073212"
|
|
||||||
sodipodi:r1="5.1116104"
|
|
||||||
sodipodi:r2="2.5558052"
|
|
||||||
sodipodi:arg1="0"
|
|
||||||
sodipodi:arg2="1.0471976"
|
|
||||||
inkscape:flatsided="false"
|
|
||||||
inkscape:rounded="0"
|
|
||||||
inkscape:randomized="0"
|
|
||||||
d="m 16.66742,60.073212 -3.833708,2.213392 -3.8337072,2.213392 0,-4.426784 0,-4.426785 3.8337082,2.213392 z" />
|
|
||||||
<path
|
|
||||||
sodipodi:nodetypes="cccc"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
id="path4615-5"
|
|
||||||
d="m 171.82574,65.174193 16.34854,0 -8.17427,-13.348454 z"
|
|
||||||
style="fill:#fbb917;fill-opacity:1;fill-rule:evenodd;stroke:#fbb917;stroke-width:1.65161395;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
|
||||||
<path
|
|
||||||
style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
d="m 179,55 0,6 2,0 0,-6"
|
|
||||||
id="path4300"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
sodipodi:nodetypes="cccc" />
|
|
||||||
<path
|
|
||||||
style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
d="m 179,62 0,2 2,0 0,-2"
|
|
||||||
id="path4300-6"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
sodipodi:nodetypes="cccc" />
|
|
||||||
<path
|
|
||||||
style="fill:#ffffff;fill-opacity:0.8;fill-rule:evenodd;stroke:#ffffff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:0.8"
|
|
||||||
d="M 99.994369,113.0221 102,114.98353 l 7,-6.9558 3,0.97227 2,-1 1,-2 0,-3 -3,3 -3,-3 3,-3 -3,0 -2,1 -1,2 0.99437,3.0221 z"
|
|
||||||
id="path4268"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
sodipodi:nodetypes="ccccccccccccccc" />
|
|
||||||
<rect
|
|
||||||
id="rect4175-3-5"
|
|
||||||
height="16"
|
|
||||||
width="16"
|
|
||||||
y="4"
|
|
||||||
x="220"
|
|
||||||
style="fill:#4c4c4c;fill-opacity:1;stroke:none;stroke-width:0" />
|
|
||||||
<path
|
|
||||||
style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
d="m 234,6 0,2 -5,5 0,5 -2,0 0,-5 -5,-5 0,-2"
|
|
||||||
id="path3546"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
sodipodi:nodetypes="cccccccc" />
|
|
||||||
<g
|
|
||||||
transform="matrix(1.3333328,0,0,-1.5999992,-139.9999,127.19999)"
|
|
||||||
id="g4383-6">
|
|
||||||
<rect
|
|
||||||
id="rect4385-2"
|
|
||||||
height="1.2499905"
|
|
||||||
width="5.9999924"
|
|
||||||
y="12.625005"
|
|
||||||
x="198.00002"
|
|
||||||
style="fill:#ffffff;fill-opacity:0.8;stroke:#000000;stroke-width:0" />
|
|
||||||
<rect
|
|
||||||
style="fill:#ffffff;fill-opacity:0.8;stroke:#000000;stroke-width:0"
|
|
||||||
x="198.00002"
|
|
||||||
y="15.125007"
|
|
||||||
width="7.4999928"
|
|
||||||
height="1.2499949"
|
|
||||||
id="rect4387-9" />
|
|
||||||
<rect
|
|
||||||
style="fill:#ffffff;fill-opacity:0.8;stroke:#000000;stroke-width:0"
|
|
||||||
x="198.00002"
|
|
||||||
y="7.6250024"
|
|
||||||
width="2.9999909"
|
|
||||||
height="1.2499905"
|
|
||||||
id="rect4389-1-0" />
|
|
||||||
<rect
|
|
||||||
style="fill:#ffffff;fill-opacity:0.8;stroke:#000000;stroke-width:0"
|
|
||||||
x="198.00002"
|
|
||||||
y="10.125004"
|
|
||||||
width="4.4999919"
|
|
||||||
height="1.2499905"
|
|
||||||
id="rect4389-1-9" />
|
|
||||||
<path
|
|
||||||
style="fill:#ffffff;fill-opacity:0.8;fill-rule:evenodd;stroke:none;stroke-width:0.68465352px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
d="m 207.00001,16.375004 0,-5.625005 -2.25,0 3,-3.1250014 3,3.1250014 -2.25,0 0,5.625005 -1.5,0"
|
|
||||||
id="path4402"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
sodipodi:nodetypes="cccccccc" />
|
|
||||||
</g>
|
|
||||||
<path
|
|
||||||
style="fill:#ffffff;fill-opacity:0.8;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
d="m 164,100 0,3 -6,6 0,7 -4,0 0,-7 -6,-6 0,-3"
|
|
||||||
id="path3546-2-2"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
sodipodi:nodetypes="cccccccc" />
|
|
||||||
<rect
|
|
||||||
style="fill:#4c4c4c;fill-opacity:1;stroke:none;stroke-width:0"
|
|
||||||
id="svg_1-3"
|
|
||||||
height="16"
|
|
||||||
width="16"
|
|
||||||
y="28"
|
|
||||||
x="4" />
|
|
||||||
<path
|
|
||||||
sodipodi:nodetypes="ccccccccc"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
id="path4402-5-7"
|
|
||||||
d="m 15,41 0,-7 -4,0 0,3 -5,-4 5,-4 0,3 6,0 0,9"
|
|
||||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.68465352px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 31 KiB |
@ -4,29 +4,13 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<!-- <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">-->
|
<!-- <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">-->
|
||||||
<title>Three js demo for particle physics</title>
|
<title>Three js demo for particle physics</title>
|
||||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
|
<link rel="stylesheet" href="css/bootstrap.min.css">
|
||||||
<link rel="stylesheet" href="css/main.css">
|
<link rel="stylesheet" href="css/main.css">
|
||||||
<script type="text/javascript" src="main.bundle.js"></script>
|
<script type="text/javascript" src="main.bundle.js"></script>
|
||||||
|
<script type="text/javascript" src ="js/jquery-3.4.1.min.js"></script>
|
||||||
|
<script type="text/javascript" src ="js/bootstrap.bundle.min.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body class="testApp">
|
<body class="application">
|
||||||
|
<div class="container-fluid" id = "app"> </div>
|
||||||
<div class="container">
|
|
||||||
<h1>Muon monitor demo</h1>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="container-fluid">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-lg-3">
|
|
||||||
<div class="row" id="tree"></div>
|
|
||||||
</div>
|
|
||||||
<div class="col-lg-6">
|
|
||||||
<div id="canvas"></div>
|
|
||||||
</div>
|
|
||||||
<div class="col-lg-3">
|
|
||||||
<div class="row" id="settings"></div>
|
|
||||||
<div class="row" id="editor"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
@ -1,7 +1,7 @@
|
|||||||
package ru.mipt.npm.muon.monitor.server
|
package ru.mipt.npm.muon.monitor.server
|
||||||
|
|
||||||
|
|
||||||
import hep.dataforge.vis.spatial.Visual3D
|
import hep.dataforge.vision.solid.SolidManager
|
||||||
import io.ktor.application.Application
|
import io.ktor.application.Application
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.application.install
|
import io.ktor.application.install
|
||||||
@ -36,7 +36,7 @@ fun Application.module() {
|
|||||||
install(DefaultHeaders)
|
install(DefaultHeaders)
|
||||||
install(CallLogging)
|
install(CallLogging)
|
||||||
install(ContentNegotiation) {
|
install(ContentNegotiation) {
|
||||||
json(module = Visual3D.serialModule)
|
json(module = SolidManager.serialModule)
|
||||||
}
|
}
|
||||||
install(Routing) {
|
install(Routing) {
|
||||||
get("/event") {
|
get("/event") {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package ru.mipt.npm.muon.monitor.sim
|
package ru.mipt.npm.muon.monitor.sim
|
||||||
|
|
||||||
import hep.dataforge.vis.spatial.Point3D
|
import hep.dataforge.vision.solid.Point3D
|
||||||
import org.apache.commons.math3.geometry.euclidean.threed.Line
|
import org.apache.commons.math3.geometry.euclidean.threed.Line
|
||||||
import org.apache.commons.math3.geometry.euclidean.threed.Plane
|
import org.apache.commons.math3.geometry.euclidean.threed.Plane
|
||||||
import org.apache.commons.math3.geometry.euclidean.threed.Vector3D
|
import org.apache.commons.math3.geometry.euclidean.threed.Vector3D
|
||||||
|
19
demo/spatial-showcase/README.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
### 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.
|
||||||
|
|
||||||
|
##### Building project
|
||||||
|
|
||||||
|
To see the JS demo: run `demo/spatial-showcase/Tasks/distribution/jsBrowserDistribution` Gradle task, then open
|
||||||
|
`build/distribuions/spatial-showcase-js-0.1.3-dev/index.html` file in your browser.
|
||||||
|
|
||||||
|
To see Java FX demo, run `demo/spatial-showcase/Tasks/application/run` Gradle task, or `main()` from `FXDemoApp.kt`.
|
||||||
|
|
||||||
|
##### Example view for JS:
|
||||||
|
|
||||||
|
![](../../doc/resources/spatial-showcase.png)
|
||||||
|
|
||||||
|
##### Example view for Java FX:
|
||||||
|
|
||||||
|
![](../../doc/resources/spatial-showcase-FX.png)
|
@ -1,6 +1,6 @@
|
|||||||
import scientifik.DependencyConfiguration
|
import scientifik.DependencyConfiguration
|
||||||
import scientifik.FXModule
|
import scientifik.FXModule
|
||||||
import scientifik.fx
|
import scientifik.useFx
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("scientifik.mpp")
|
id("scientifik.mpp")
|
||||||
@ -8,7 +8,7 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val fxVersion: String by rootProject.extra
|
val fxVersion: String by rootProject.extra
|
||||||
fx(FXModule.CONTROLS, version = fxVersion, configuration = DependencyConfiguration.IMPLEMENTATION)
|
useFx(FXModule.CONTROLS, version = fxVersion, configuration = DependencyConfiguration.IMPLEMENTATION)
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
|
|
||||||
@ -19,13 +19,13 @@ kotlin {
|
|||||||
sourceSets {
|
sourceSets {
|
||||||
commonMain {
|
commonMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
api(project(":dataforge-vis-spatial"))
|
api(project(":visionforge-solid"))
|
||||||
api(project(":dataforge-vis-spatial-gdml"))
|
api(project(":visionforge-gdml"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
application {
|
application {
|
||||||
mainClassName = "hep.dataforge.vis.spatial.demo.FXDemoAppKt"
|
mainClassName = "hep.dataforge.vision.solid.demo.FXDemoAppKt"
|
||||||
}
|
}
|
@ -1,13 +1,13 @@
|
|||||||
package hep.dataforge.vis.spatial.demo
|
package hep.dataforge.vision.solid.demo
|
||||||
|
|
||||||
import hep.dataforge.meta.Meta
|
import hep.dataforge.meta.Meta
|
||||||
import hep.dataforge.meta.invoke
|
import hep.dataforge.meta.invoke
|
||||||
import hep.dataforge.names.toName
|
import hep.dataforge.names.toName
|
||||||
import hep.dataforge.output.OutputManager
|
import hep.dataforge.output.OutputManager
|
||||||
import hep.dataforge.vis.Colors
|
import hep.dataforge.vision.Colors
|
||||||
import hep.dataforge.vis.VisualObject
|
import hep.dataforge.vision.Vision
|
||||||
import hep.dataforge.vis.spatial.*
|
import hep.dataforge.vision.solid.*
|
||||||
import hep.dataforge.vis.spatial.specifications.Canvas
|
import hep.dataforge.vision.solid.specifications.Canvas3DOptions
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlin.math.PI
|
import kotlin.math.PI
|
||||||
import kotlin.math.cos
|
import kotlin.math.cos
|
||||||
@ -15,15 +15,15 @@ import kotlin.math.sin
|
|||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
|
|
||||||
fun OutputManager.demo(name: String, title: String = name, block: VisualGroup3D.() -> Unit) {
|
fun OutputManager.demo(name: String, title: String = name, block: SolidGroup.() -> Unit) {
|
||||||
val meta = Meta {
|
val meta = Meta {
|
||||||
"title" put title
|
"title" put title
|
||||||
}
|
}
|
||||||
val output = get(VisualObject::class, name.toName(), meta = meta)
|
val output = get(Vision::class, name.toName(), meta = meta)
|
||||||
output.render (action = block)
|
output.render (action = block)
|
||||||
}
|
}
|
||||||
|
|
||||||
val canvasOptions = Canvas {
|
val canvasOptions = Canvas3DOptions {
|
||||||
minSize = 500
|
minSize = 500
|
||||||
axes {
|
axes {
|
||||||
size = 500.0
|
size = 500.0
|
@ -1,4 +1,4 @@
|
|||||||
package hep.dataforge.vis.spatial.demo
|
package hep.dataforge.vision.solid.demo
|
||||||
|
|
||||||
import hep.dataforge.js.Application
|
import hep.dataforge.js.Application
|
||||||
import hep.dataforge.js.startApplication
|
import hep.dataforge.js.startApplication
|
@ -1,4 +1,4 @@
|
|||||||
package hep.dataforge.vis.spatial.demo
|
package hep.dataforge.vision.solid.demo
|
||||||
|
|
||||||
import hep.dataforge.context.Global
|
import hep.dataforge.context.Global
|
||||||
import hep.dataforge.meta.Meta
|
import hep.dataforge.meta.Meta
|
||||||
@ -7,10 +7,10 @@ import hep.dataforge.meta.string
|
|||||||
import hep.dataforge.names.Name
|
import hep.dataforge.names.Name
|
||||||
import hep.dataforge.output.OutputManager
|
import hep.dataforge.output.OutputManager
|
||||||
import hep.dataforge.output.Renderer
|
import hep.dataforge.output.Renderer
|
||||||
import hep.dataforge.vis.VisualObject
|
import hep.dataforge.vision.Vision
|
||||||
import hep.dataforge.vis.spatial.three.ThreeCanvas
|
import hep.dataforge.vision.solid.three.ThreeCanvas
|
||||||
import hep.dataforge.vis.spatial.three.ThreePlugin
|
import hep.dataforge.vision.solid.three.ThreePlugin
|
||||||
import hep.dataforge.vis.spatial.three.output
|
import hep.dataforge.vision.solid.three.output
|
||||||
import kotlinx.html.dom.append
|
import kotlinx.html.dom.append
|
||||||
import kotlinx.html.dom.create
|
import kotlinx.html.dom.create
|
||||||
import kotlinx.html.h2
|
import kotlinx.html.h2
|
||||||
@ -39,7 +39,7 @@ class ThreeDemoGrid(element: Element, meta: Meta = Meta.EMPTY) : OutputManager {
|
|||||||
override fun <T : Any> get(type: KClass<out T>, name: Name, stage: Name, meta: Meta): Renderer<T> {
|
override fun <T : Any> get(type: KClass<out T>, name: Name, stage: Name, meta: Meta): Renderer<T> {
|
||||||
|
|
||||||
return outputs.getOrPut(name) {
|
return outputs.getOrPut(name) {
|
||||||
if (type != VisualObject::class) error("Supports only DisplayObject")
|
if (type != Vision::class) error("Supports only DisplayObject")
|
||||||
lateinit var output: ThreeCanvas
|
lateinit var output: ThreeCanvas
|
||||||
//TODO calculate cell width here using jquery
|
//TODO calculate cell width here using jquery
|
||||||
gridRoot.append {
|
gridRoot.append {
|
@ -1,20 +1,20 @@
|
|||||||
@file:UseSerializers(Point3DSerializer::class)
|
@file:UseSerializers(Point3DSerializer::class)
|
||||||
|
|
||||||
package hep.dataforge.vis.spatial.demo
|
package hep.dataforge.vision.solid.demo
|
||||||
|
|
||||||
import hep.dataforge.meta.int
|
import hep.dataforge.meta.int
|
||||||
import hep.dataforge.meta.number
|
import hep.dataforge.meta.number
|
||||||
import hep.dataforge.meta.setProperty
|
import hep.dataforge.meta.setItem
|
||||||
import hep.dataforge.names.plus
|
import hep.dataforge.names.plus
|
||||||
import hep.dataforge.names.startsWith
|
import hep.dataforge.names.startsWith
|
||||||
import hep.dataforge.values.asValue
|
import hep.dataforge.values.asValue
|
||||||
import hep.dataforge.vis.getProperty
|
import hep.dataforge.vision.getProperty
|
||||||
import hep.dataforge.vis.set
|
import hep.dataforge.vision.set
|
||||||
import hep.dataforge.vis.spatial.*
|
import hep.dataforge.vision.solid.*
|
||||||
import hep.dataforge.vis.spatial.VisualObject3D.Companion.GEOMETRY_KEY
|
import hep.dataforge.vision.solid.Solid.Companion.GEOMETRY_KEY
|
||||||
import hep.dataforge.vis.spatial.demo.VariableBoxThreeFactory.Z_SIZE_KEY
|
import hep.dataforge.vision.solid.demo.VariableBoxThreeFactory.Z_SIZE_KEY
|
||||||
import hep.dataforge.vis.spatial.three.*
|
import hep.dataforge.vision.solid.three.*
|
||||||
import hep.dataforge.vis.spatial.three.ThreeMaterials.getMaterial
|
import hep.dataforge.vision.solid.three.ThreeMaterials.getMaterial
|
||||||
import info.laht.threekt.core.BufferGeometry
|
import info.laht.threekt.core.BufferGeometry
|
||||||
import info.laht.threekt.core.Object3D
|
import info.laht.threekt.core.Object3D
|
||||||
import info.laht.threekt.geometries.BoxBufferGeometry
|
import info.laht.threekt.geometries.BoxBufferGeometry
|
||||||
@ -23,16 +23,16 @@ import kotlinx.serialization.UseSerializers
|
|||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
internal var VisualObject3D.variableZSize: Number
|
internal var Solid.variableZSize: Number
|
||||||
get() = getProperty(Z_SIZE_KEY, false).number ?: 0f
|
get() = getProperty(Z_SIZE_KEY, false).number ?: 0f
|
||||||
set(value) {
|
set(value) {
|
||||||
setProperty(Z_SIZE_KEY, value.asValue())
|
setItem(Z_SIZE_KEY, value.asValue())
|
||||||
}
|
}
|
||||||
|
|
||||||
internal var VisualObject3D.value: Int
|
internal var Solid.value: Int
|
||||||
get() = getProperty("value", false).int ?: 0
|
get() = getProperty("value", false).int ?: 0
|
||||||
set(value) {
|
set(value) {
|
||||||
setProperty("value", value.asValue())
|
setItem("value", value.asValue())
|
||||||
val size = value.toFloat() / 255f * 20f
|
val size = value.toFloat() / 255f * 20f
|
||||||
scaleZ = size
|
scaleZ = size
|
||||||
z = -size / 2
|
z = -size / 2
|
||||||
@ -43,26 +43,26 @@ internal var VisualObject3D.value: Int
|
|||||||
color(r.toUByte(), g.toUByte(), b.toUByte())
|
color(r.toUByte(), g.toUByte(), b.toUByte())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun VisualGroup3D.varBox(
|
fun SolidGroup.varBox(
|
||||||
xSize: Number,
|
xSize: Number,
|
||||||
ySize: Number,
|
ySize: Number,
|
||||||
zSize: Number,
|
zSize: Number,
|
||||||
name: String = "",
|
name: String = "",
|
||||||
action: VisualObject3D.() -> Unit = {}
|
action: Solid.() -> Unit = {}
|
||||||
) = CustomThreeVisualObject(VariableBoxThreeFactory).apply {
|
) = CustomThreeVision(VariableBoxThreeFactory).apply {
|
||||||
scaleX = xSize
|
scaleX = xSize
|
||||||
scaleY = ySize
|
scaleY = ySize
|
||||||
scaleZ = zSize
|
scaleZ = zSize
|
||||||
}.apply(action).also { set(name, it) }
|
}.apply(action).also { set(name, it) }
|
||||||
|
|
||||||
private object VariableBoxThreeFactory : ThreeFactory<VisualObject3D> {
|
private object VariableBoxThreeFactory : ThreeFactory<Solid> {
|
||||||
val X_SIZE_KEY = GEOMETRY_KEY + "xSize"
|
val X_SIZE_KEY = GEOMETRY_KEY + "xSize"
|
||||||
val Y_SIZE_KEY = GEOMETRY_KEY + "ySize"
|
val Y_SIZE_KEY = GEOMETRY_KEY + "ySize"
|
||||||
val Z_SIZE_KEY = GEOMETRY_KEY + "zSize"
|
val Z_SIZE_KEY = GEOMETRY_KEY + "zSize"
|
||||||
|
|
||||||
override val type: KClass<in VisualObject3D> get() = VisualObject3D::class
|
override val type: KClass<in Solid> get() = Solid::class
|
||||||
|
|
||||||
override fun invoke(obj: VisualObject3D): Object3D {
|
override fun invoke(obj: Solid): Object3D {
|
||||||
val xSize = obj.getProperty(X_SIZE_KEY, false).number?.toDouble() ?: 1.0
|
val xSize = obj.getProperty(X_SIZE_KEY, false).number?.toDouble() ?: 1.0
|
||||||
val ySize = obj.getProperty(Y_SIZE_KEY, false).number?.toDouble() ?: 1.0
|
val ySize = obj.getProperty(Y_SIZE_KEY, false).number?.toDouble() ?: 1.0
|
||||||
val zSize = obj.getProperty(Z_SIZE_KEY, false).number?.toDouble() ?: 1.0
|
val zSize = obj.getProperty(Z_SIZE_KEY, false).number?.toDouble() ?: 1.0
|
||||||
@ -87,7 +87,7 @@ private object VariableBoxThreeFactory : ThreeFactory<VisualObject3D> {
|
|||||||
mesh.scale.set(xSize, ySize, zSize)
|
mesh.scale.set(xSize, ySize, zSize)
|
||||||
|
|
||||||
//add listener to object properties
|
//add listener to object properties
|
||||||
obj.onPropertyChange(this) { name, _, _ ->
|
obj.onPropertyChange(this) { name ->
|
||||||
when {
|
when {
|
||||||
// name.startsWith(GEOMETRY_KEY) -> {
|
// name.startsWith(GEOMETRY_KEY) -> {
|
||||||
// val newXSize = obj.getProperty(X_SIZE_KEY, false).number?.toDouble() ?: 1.0
|
// val newXSize = obj.getProperty(X_SIZE_KEY, false).number?.toDouble() ?: 1.0
|
@ -8,7 +8,7 @@
|
|||||||
integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
|
integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
|
||||||
<script type="text/javascript" src="main.bundle.js"></script>
|
<script type="text/javascript" src="main.bundle.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body class="testApp">
|
<body class="application">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1>Demo grid</h1>
|
<h1>Demo grid</h1>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,28 +0,0 @@
|
|||||||
package hep.dataforge.vis.spatial.demo
|
|
||||||
|
|
||||||
import hep.dataforge.vis.spatial.gdml.gdml
|
|
||||||
import javafx.stage.Stage
|
|
||||||
import tornadofx.*
|
|
||||||
import java.nio.file.Paths
|
|
||||||
|
|
||||||
class FXDemoApp : App(FXDemoGrid::class) {
|
|
||||||
|
|
||||||
val view: FXDemoGrid by inject()
|
|
||||||
|
|
||||||
override fun start(stage: Stage) {
|
|
||||||
super.start(stage)
|
|
||||||
|
|
||||||
stage.width = 600.0
|
|
||||||
stage.height = 600.0
|
|
||||||
|
|
||||||
view.showcase()
|
|
||||||
view.demo("gdml", "gdml-cubes") {
|
|
||||||
gdml(Paths.get("D:\\Work\\Projects\\gdml.kt\\gdml-source\\cubes.gdml"))
|
|
||||||
//setProperty(Material3D.MATERIAL_WIREFRAME_KEY, true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun main() {
|
|
||||||
launch<FXDemoApp>()
|
|
||||||
}
|
|
@ -0,0 +1,35 @@
|
|||||||
|
package hep.dataforge.vision.solid.demo
|
||||||
|
|
||||||
|
import hep.dataforge.vision.gdml.gdml
|
||||||
|
import javafx.stage.Stage
|
||||||
|
import tornadofx.*
|
||||||
|
import java.nio.file.NoSuchFileException
|
||||||
|
import java.nio.file.Paths
|
||||||
|
|
||||||
|
class FXDemoApp : App(FXDemoGrid::class) {
|
||||||
|
|
||||||
|
val view: FXDemoGrid by inject()
|
||||||
|
|
||||||
|
override fun start(stage: Stage) {
|
||||||
|
super.start(stage)
|
||||||
|
|
||||||
|
stage.width = 600.0
|
||||||
|
stage.height = 600.0
|
||||||
|
|
||||||
|
view.showcase()
|
||||||
|
try {
|
||||||
|
view.demo("gdml", "gdml-cubes") {
|
||||||
|
gdml(Paths.get("D:\\Work\\Projects\\gdml.kt\\gdml-source\\cubes.gdml"))
|
||||||
|
//setProperty(Material3D.MATERIAL_WIREFRAME_KEY, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e: NoSuchFileException) {
|
||||||
|
println("GDML demo: Please specify the correct file path e.g. " +
|
||||||
|
"visionforge\\demo\\gdml\\src\\commonMain\\resources\\cubes.gdml")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun main() {
|
||||||
|
launch<FXDemoApp>()
|
||||||
|
}
|
@ -1,16 +1,13 @@
|
|||||||
package hep.dataforge.vis.spatial.demo
|
package hep.dataforge.vision.solid.demo
|
||||||
|
|
||||||
import hep.dataforge.context.Global
|
import hep.dataforge.context.Global
|
||||||
import hep.dataforge.meta.Meta
|
import hep.dataforge.meta.Meta
|
||||||
import hep.dataforge.meta.buildMeta
|
|
||||||
import hep.dataforge.names.Name
|
import hep.dataforge.names.Name
|
||||||
import hep.dataforge.output.OutputManager
|
import hep.dataforge.output.OutputManager
|
||||||
import hep.dataforge.output.Renderer
|
import hep.dataforge.output.Renderer
|
||||||
import hep.dataforge.vis.VisualObject
|
import hep.dataforge.vision.Vision
|
||||||
import hep.dataforge.vis.spatial.fx.FX3DPlugin
|
import hep.dataforge.vision.solid.fx.FX3DPlugin
|
||||||
import hep.dataforge.vis.spatial.fx.FXCanvas3D
|
import hep.dataforge.vision.solid.fx.FXCanvas3D
|
||||||
import hep.dataforge.vis.spatial.specifications.Axes as AxesSpec
|
|
||||||
import hep.dataforge.vis.spatial.specifications.Canvas as CanvasSpec
|
|
||||||
import javafx.collections.FXCollections
|
import javafx.collections.FXCollections
|
||||||
import javafx.scene.Parent
|
import javafx.scene.Parent
|
||||||
import javafx.scene.control.Tab
|
import javafx.scene.control.Tab
|
||||||
@ -33,7 +30,7 @@ class FXDemoGrid : View(title = "DataForge-vis FX demo"), OutputManager {
|
|||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
override fun <T : Any> get(type: KClass<out T>, name: Name, stage: Name, meta: Meta): Renderer<T> {
|
override fun <T : Any> get(type: KClass<out T>, name: Name, stage: Name, meta: Meta): Renderer<T> {
|
||||||
return outputs.getOrPut(name) {
|
return outputs.getOrPut(name) {
|
||||||
if (type != VisualObject::class) kotlin.error("Supports only DisplayObject")
|
if (type != Vision::class) kotlin.error("Supports only DisplayObject")
|
||||||
val output = FXCanvas3D(fx3d, canvasOptions)
|
val output = FXCanvas3D(fx3d, canvasOptions)
|
||||||
|
|
||||||
output
|
output
|
Before Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 6.0 KiB |
BIN
doc/resources/class-diag-core.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
doc/resources/class-diag-solid.png
Normal file
After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 87 KiB |
BIN
doc/resources/spatial-showcase-FX.png
Normal file
After Width: | Height: | Size: 28 KiB |
@ -19,8 +19,9 @@ kotlin {
|
|||||||
sourceSets {
|
sourceSets {
|
||||||
commonMain {
|
commonMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
api(project(":dataforge-vis-spatial"))
|
api(project(":visionforge-solid"))
|
||||||
api(project(":dataforge-vis-spatial-gdml"))
|
api(project(":visionforge-gdml"))
|
||||||
|
api(project(":ui:bootstrap"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,14 +2,14 @@ import hep.dataforge.context.Global
|
|||||||
import hep.dataforge.js.Application
|
import hep.dataforge.js.Application
|
||||||
import hep.dataforge.js.startApplication
|
import hep.dataforge.js.startApplication
|
||||||
import hep.dataforge.names.Name
|
import hep.dataforge.names.Name
|
||||||
import hep.dataforge.vis.editor.objectTree
|
import hep.dataforge.vision.bootstrap.objectTree
|
||||||
import hep.dataforge.vis.editor.visualPropertyEditor
|
import hep.dataforge.vision.bootstrap.visualPropertyEditor
|
||||||
import hep.dataforge.vis.spatial.Point3D
|
import hep.dataforge.vision.solid.Point3D
|
||||||
import hep.dataforge.vis.spatial.VisualGroup3D
|
import hep.dataforge.vision.solid.SolidGroup
|
||||||
import hep.dataforge.vis.spatial.box
|
import hep.dataforge.vision.solid.box
|
||||||
import hep.dataforge.vis.spatial.group
|
import hep.dataforge.vision.solid.group
|
||||||
import hep.dataforge.vis.spatial.three.ThreePlugin
|
import hep.dataforge.vision.solid.three.ThreePlugin
|
||||||
import hep.dataforge.vis.spatial.three.threeCanvas
|
import hep.dataforge.vision.solid.three.threeCanvas
|
||||||
import org.w3c.dom.HTMLElement
|
import org.w3c.dom.HTMLElement
|
||||||
import react.dom.div
|
import react.dom.div
|
||||||
import react.dom.render
|
import react.dom.render
|
||||||
@ -24,7 +24,7 @@ private class PlayGroundApp : Application {
|
|||||||
val element =
|
val element =
|
||||||
document.getElementById("app") as? HTMLElement ?: error("Element with id 'canvas' not found on page")
|
document.getElementById("app") as? HTMLElement ?: error("Element with id 'canvas' not found on page")
|
||||||
|
|
||||||
val obj = VisualGroup3D().apply {
|
val obj = SolidGroup().apply {
|
||||||
box(100, 100, 100, name = "A")
|
box(100, 100, 100, name = "A")
|
||||||
group("B") {
|
group("B") {
|
||||||
position = Point3D(120, 0, 0)
|
position = Point3D(120, 0, 0)
|
||||||
@ -45,8 +45,6 @@ private class PlayGroundApp : Application {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
<link rel="stylesheet" href="css/common.css">
|
<link rel="stylesheet" href="css/common.css">
|
||||||
<script type="text/javascript" src="playground.js"></script>
|
<script type="text/javascript" src="playground.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body class="testApp">
|
<body class="application">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1>Playground</h1>
|
<h1>Playground</h1>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
pluginManagement {
|
pluginManagement {
|
||||||
|
val kotlinVersion = "1.3.72"
|
||||||
|
val toolsVersion = "0.5.2"
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenLocal()
|
mavenLocal()
|
||||||
jcenter()
|
jcenter()
|
||||||
@ -10,11 +13,18 @@ pluginManagement {
|
|||||||
maven("https://dl.bintray.com/mipt-npm/dev")
|
maven("https://dl.bintray.com/mipt-npm/dev")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
kotlin("jvm") version kotlinVersion
|
||||||
|
id("scientifik.mpp") version toolsVersion
|
||||||
|
id("scientifik.jvm") version toolsVersion
|
||||||
|
id("scientifik.js") version toolsVersion
|
||||||
|
id("scientifik.publish") version toolsVersion
|
||||||
|
}
|
||||||
|
|
||||||
resolutionStrategy {
|
resolutionStrategy {
|
||||||
eachPlugin {
|
eachPlugin {
|
||||||
when (requested.id.id) {
|
when (requested.id.id) {
|
||||||
"scientifik.mpp", "scientifik.publish", "scientifik.jvm", "scientifik.js" -> useModule("scientifik:gradle-tools:${requested.version}")
|
"scientifik.mpp", "scientifik.publish", "scientifik.jvm", "scientifik.js" -> useModule("scientifik:gradle-tools:${toolsVersion}")
|
||||||
"org.openjfx.javafxplugin" -> useModule("org.openjfx:javafx-plugin:${requested.version}")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -22,24 +32,19 @@ pluginManagement {
|
|||||||
|
|
||||||
//enableFeaturePreview("GRADLE_METADATA")
|
//enableFeaturePreview("GRADLE_METADATA")
|
||||||
|
|
||||||
rootProject.name = "dataforge-vis"
|
rootProject.name = "visionforge"
|
||||||
|
|
||||||
include(
|
include(
|
||||||
":dataforge-vis-common",
|
":ui",
|
||||||
":dataforge-vis-spatial",
|
":ui:react",
|
||||||
":dataforge-vis-spatial-gdml",
|
":ui:ring",
|
||||||
|
":ui:material",
|
||||||
|
":ui:bootstrap",
|
||||||
|
":visionforge-core",
|
||||||
|
":visionforge-solid",
|
||||||
|
":visionforge-gdml",
|
||||||
":demo:spatial-showcase",
|
":demo:spatial-showcase",
|
||||||
":demo:gdml",
|
":demo:gdml",
|
||||||
":demo:muon-monitor",
|
":demo:muon-monitor",
|
||||||
":playground"
|
":playground"
|
||||||
)
|
)
|
||||||
|
|
||||||
//if(file("../dataforge-core").exists()) {
|
|
||||||
// includeBuild("../dataforge-core"){
|
|
||||||
// dependencySubstitution {
|
|
||||||
// //substitute(module("hep.dataforge:dataforge-output")).with(project(":dataforge-output"))
|
|
||||||
// substitute(module("hep.dataforge:dataforge-output-jvm")).with(project(":dataforge-output"))
|
|
||||||
// substitute(module("hep.dataforge:dataforge-output-js")).with(project(":dataforge-output"))
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//}
|
|
15
ui/bootstrap/build.gradle.kts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
plugins {
|
||||||
|
id("scientifik.js")
|
||||||
|
}
|
||||||
|
|
||||||
|
val dataforgeVersion: String by rootProject.extra
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
target {
|
||||||
|
useCommonJs()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies{
|
||||||
|
api(project(":ui:react"))
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package hep.dataforge.vis.editor
|
package hep.dataforge.vision.bootstrap
|
||||||
|
|
||||||
import hep.dataforge.meta.Meta
|
import hep.dataforge.meta.Meta
|
||||||
import hep.dataforge.meta.MetaItem
|
import hep.dataforge.meta.MetaItem
|
||||||
@ -34,10 +34,10 @@ class MetaViewerComponent : RComponent<MetaViewerProps, TreeState>() {
|
|||||||
override fun RBuilder.render() {
|
override fun RBuilder.render() {
|
||||||
div("d-inline-block text-truncate") {
|
div("d-inline-block text-truncate") {
|
||||||
if (props.meta.items.isNotEmpty()) {
|
if (props.meta.items.isNotEmpty()) {
|
||||||
span("objTree-caret") {
|
span("tree-caret") {
|
||||||
attrs {
|
attrs {
|
||||||
if (state.expanded) {
|
if (state.expanded) {
|
||||||
classes += "objTree-caret-down"
|
classes += "tree-caret-down"
|
||||||
}
|
}
|
||||||
onClickFunction = onClick
|
onClickFunction = onClick
|
||||||
}
|
}
|
@ -0,0 +1,127 @@
|
|||||||
|
package hep.dataforge.vision.bootstrap
|
||||||
|
|
||||||
|
import hep.dataforge.names.Name
|
||||||
|
import hep.dataforge.names.plus
|
||||||
|
import hep.dataforge.names.startsWith
|
||||||
|
import hep.dataforge.vision.Vision
|
||||||
|
import hep.dataforge.vision.VisionGroup
|
||||||
|
import hep.dataforge.vision.isEmpty
|
||||||
|
import hep.dataforge.vision.react.RFBuilder
|
||||||
|
import hep.dataforge.vision.react.component
|
||||||
|
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 selected: Name?
|
||||||
|
var obj: Vision
|
||||||
|
var clickCallback: (Name) -> Unit
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TreeState : RState {
|
||||||
|
var expanded: Boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun RFBuilder.objectTree(props: ObjectTreeProps): Unit {
|
||||||
|
var expanded: Boolean by useState{ props.selected?.startsWith(props.name) ?: false }
|
||||||
|
|
||||||
|
val onClick: (Event) -> Unit = {
|
||||||
|
expanded = !expanded
|
||||||
|
}
|
||||||
|
|
||||||
|
fun RBuilder.treeLabel(text: String) {
|
||||||
|
button(classes = "btn btn-link align-middle tree-label p-0") {
|
||||||
|
+text
|
||||||
|
attrs {
|
||||||
|
if (props.name == props.selected) {
|
||||||
|
classes += "tree-label-selected"
|
||||||
|
}
|
||||||
|
onClickFunction = { props.clickCallback(props.name) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val token = props.name.last()?.toString() ?: "World"
|
||||||
|
val obj = props.obj
|
||||||
|
|
||||||
|
//display as node if any child is visible
|
||||||
|
if (obj is VisionGroup) {
|
||||||
|
div("d-block text-truncate") {
|
||||||
|
if (obj.children.any { !it.key.body.startsWith("@") }) {
|
||||||
|
span("tree-caret") {
|
||||||
|
attrs {
|
||||||
|
if (expanded) {
|
||||||
|
classes += "tree-caret-down"
|
||||||
|
}
|
||||||
|
onClickFunction = onClick
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
treeLabel(token)
|
||||||
|
}
|
||||||
|
if (expanded) {
|
||||||
|
ul("tree") {
|
||||||
|
obj.children.entries
|
||||||
|
.filter { !it.key.toString().startsWith("@") } // ignore statics and other hidden children
|
||||||
|
.sortedBy { (it.value as? VisionGroup)?.isEmpty ?: true }
|
||||||
|
.forEach { (childToken, child) ->
|
||||||
|
li("tree-item") {
|
||||||
|
child(ObjectTree) {
|
||||||
|
attrs {
|
||||||
|
this.name = props.name + childToken
|
||||||
|
this.obj = child
|
||||||
|
this.selected = props.selected
|
||||||
|
this.clickCallback = props.clickCallback
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
div("d-block text-truncate") {
|
||||||
|
span("tree-leaf") {}
|
||||||
|
treeLabel(token)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val ObjectTree: FunctionalComponent<ObjectTreeProps> = component { props ->
|
||||||
|
objectTree(props)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Element.renderObjectTree(
|
||||||
|
vision: Vision,
|
||||||
|
clickCallback: (Name) -> Unit = {}
|
||||||
|
) = render(this) {
|
||||||
|
card("Object tree") {
|
||||||
|
child(ObjectTree) {
|
||||||
|
attrs {
|
||||||
|
this.name = Name.EMPTY
|
||||||
|
this.obj = vision
|
||||||
|
this.selected = null
|
||||||
|
this.clickCallback = clickCallback
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun RBuilder.objectTree(
|
||||||
|
vision: Vision,
|
||||||
|
selected: Name? = null,
|
||||||
|
clickCallback: (Name) -> Unit = {}
|
||||||
|
) {
|
||||||
|
child(ObjectTree) {
|
||||||
|
attrs {
|
||||||
|
this.name = Name.EMPTY
|
||||||
|
this.obj = vision
|
||||||
|
this.selected = selected
|
||||||
|
this.clickCallback = clickCallback
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,208 @@
|
|||||||
|
package hep.dataforge.vision.bootstrap
|
||||||
|
|
||||||
|
import hep.dataforge.names.Name
|
||||||
|
import hep.dataforge.names.NameToken
|
||||||
|
import kotlinx.html.*
|
||||||
|
import kotlinx.html.js.div
|
||||||
|
import kotlinx.html.js.onClickFunction
|
||||||
|
import org.w3c.dom.HTMLElement
|
||||||
|
import react.RBuilder
|
||||||
|
import react.ReactElement
|
||||||
|
import react.dom.*
|
||||||
|
|
||||||
|
inline fun TagConsumer<HTMLElement>.card(title: String, crossinline block: TagConsumer<HTMLElement>.() -> Unit) {
|
||||||
|
div("card w-100") {
|
||||||
|
div("card-body") {
|
||||||
|
h3(classes = "card-title") { +title }
|
||||||
|
block()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun RBuilder.card(title: String, classes: String? = null, crossinline block: RBuilder.() -> Unit) {
|
||||||
|
div("card w-100 $classes") {
|
||||||
|
div("card-body") {
|
||||||
|
h3(classes = "card-title") {
|
||||||
|
+title
|
||||||
|
}
|
||||||
|
block()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun TagConsumer<HTMLElement>.accordion(id: String, elements: List<Pair<String, DIV.() -> Unit>>) {
|
||||||
|
div("container-fluid") {
|
||||||
|
div("accordion") {
|
||||||
|
this.id = id
|
||||||
|
elements.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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typealias AccordionBuilder = MutableList<Pair<String, DIV.() -> Unit>>
|
||||||
|
|
||||||
|
fun AccordionBuilder.entry(title: String, builder: DIV.() -> Unit) {
|
||||||
|
add(title to builder)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun TagConsumer<HTMLElement>.accordion(id: String, builder: AccordionBuilder.() -> Unit) {
|
||||||
|
val list = ArrayList<Pair<String, DIV.() -> Unit>>().apply(builder)
|
||||||
|
accordion(id, list)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun RBuilder.accordion(id: String, elements: List<Pair<String, RDOMBuilder<DIV>.() -> Unit>>): ReactElement {
|
||||||
|
return div("container-fluid") {
|
||||||
|
div("accordion") {
|
||||||
|
attrs {
|
||||||
|
this.id = id
|
||||||
|
}
|
||||||
|
elements.forEachIndexed { index, (title, builder) ->
|
||||||
|
val headerID = "${id}-${index}-heading"
|
||||||
|
val collapseID = "${id}-${index}-collapse"
|
||||||
|
div("card p-0 m-0") {
|
||||||
|
div("card-header") {
|
||||||
|
attrs {
|
||||||
|
this.id = headerID
|
||||||
|
}
|
||||||
|
h5("mb-0") {
|
||||||
|
button(classes = "btn btn-link collapsed", type = ButtonType.button) {
|
||||||
|
attrs {
|
||||||
|
attributes["data-toggle"] = "collapse"
|
||||||
|
attributes["data-target"] = "#$collapseID"
|
||||||
|
attributes["aria-expanded"] = "false"
|
||||||
|
attributes["aria-controls"] = collapseID
|
||||||
|
}
|
||||||
|
+title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
div("collapse") {
|
||||||
|
attrs {
|
||||||
|
this.id = collapseID
|
||||||
|
attributes["aria-labelledby"] = headerID
|
||||||
|
attributes["data-parent"] = "#$id"
|
||||||
|
}
|
||||||
|
div("card-body", block = builder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun RBuilder.namecrumbs(name: Name?, rootTitle: String, link: (Name) -> Unit) {
|
||||||
|
div("container-fluid p-0") {
|
||||||
|
nav {
|
||||||
|
attrs {
|
||||||
|
attributes["aria-label"] = "breadcrumb"
|
||||||
|
}
|
||||||
|
ol("breadcrumb") {
|
||||||
|
li("breadcrumb-item") {
|
||||||
|
button(classes = "btn btn-link p-0") {
|
||||||
|
+rootTitle
|
||||||
|
attrs {
|
||||||
|
onClickFunction = {
|
||||||
|
link(Name.EMPTY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (name != null) {
|
||||||
|
val tokens = ArrayList<NameToken>(name.length)
|
||||||
|
name.tokens.forEach { token ->
|
||||||
|
tokens.add(token)
|
||||||
|
val fullName = Name(tokens.toList())
|
||||||
|
li("breadcrumb-item") {
|
||||||
|
button(classes = "btn btn-link p-0") {
|
||||||
|
+token.toString()
|
||||||
|
attrs {
|
||||||
|
onClickFunction = {
|
||||||
|
console.log("Selected = $fullName")
|
||||||
|
link(fullName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typealias RAccordionBuilder = MutableList<Pair<String, RDOMBuilder<DIV>.() -> Unit>>
|
||||||
|
|
||||||
|
fun RAccordionBuilder.entry(title: String, builder: RDOMBuilder<DIV>.() -> Unit) {
|
||||||
|
add(title to builder)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun RBuilder.accordion(id: String, builder: RAccordionBuilder.() -> Unit): ReactElement {
|
||||||
|
val list = ArrayList<Pair<String, RDOMBuilder<DIV>.() -> Unit>>().apply(builder)
|
||||||
|
return accordion(id, list)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun joinStyles(vararg styles: String?) = styles.joinToString(separator = " ") { it ?: "" }
|
||||||
|
|
||||||
|
enum class ContainerSize(val suffix: String) {
|
||||||
|
DEFAULT(""),
|
||||||
|
SM("-sm"),
|
||||||
|
MD("-md"),
|
||||||
|
LG("-lg"),
|
||||||
|
XL("-xl"),
|
||||||
|
FLUID("-fluid")
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun RBuilder.container(
|
||||||
|
classes: String? = null,
|
||||||
|
size: ContainerSize = ContainerSize.FLUID,
|
||||||
|
block: RDOMBuilder<DIV>.() -> Unit
|
||||||
|
): ReactElement = div(joinStyles(classes, "container${size.suffix}"), block)
|
||||||
|
|
||||||
|
|
||||||
|
enum class GridMaxSize(val suffix: String) {
|
||||||
|
NONE(""),
|
||||||
|
SM("-sm"),
|
||||||
|
MD("-md"),
|
||||||
|
LG("-lg"),
|
||||||
|
XL("-xl")
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun RBuilder.gridColumn(
|
||||||
|
weight: Int? = null,
|
||||||
|
classes: String? = null,
|
||||||
|
maxSize: GridMaxSize = GridMaxSize.NONE,
|
||||||
|
block: RDOMBuilder<DIV>.() -> Unit
|
||||||
|
): ReactElement {
|
||||||
|
val weightSuffix = weight?.let { "-$it" } ?: ""
|
||||||
|
return div(joinStyles(classes, "col${maxSize.suffix}$weightSuffix"), block)
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun RBuilder.gridRow(
|
||||||
|
classes: String? = null,
|
||||||
|
block: RDOMBuilder<DIV>.() -> Unit
|
||||||
|
): ReactElement {
|
||||||
|
return div(joinStyles(classes, "row"), block)
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
package hep.dataforge.vision.bootstrap
|
||||||
|
|
||||||
|
import hep.dataforge.meta.Meta
|
||||||
|
import hep.dataforge.meta.descriptors.NodeDescriptor
|
||||||
|
import hep.dataforge.names.Name
|
||||||
|
import hep.dataforge.names.isEmpty
|
||||||
|
import hep.dataforge.vision.Vision
|
||||||
|
import hep.dataforge.vision.react.configEditor
|
||||||
|
import org.w3c.dom.Element
|
||||||
|
import react.RBuilder
|
||||||
|
import react.dom.li
|
||||||
|
import react.dom.nav
|
||||||
|
import react.dom.ol
|
||||||
|
import react.dom.render
|
||||||
|
import kotlin.collections.set
|
||||||
|
|
||||||
|
fun RBuilder.visualPropertyEditor(
|
||||||
|
path: Name,
|
||||||
|
item: Vision,
|
||||||
|
descriptor: NodeDescriptor? = item.descriptor,
|
||||||
|
default: Meta? = null
|
||||||
|
) {
|
||||||
|
card("Properties") {
|
||||||
|
if (!path.isEmpty()) {
|
||||||
|
nav {
|
||||||
|
attrs {
|
||||||
|
attributes["aria-label"] = "breadcrumb"
|
||||||
|
}
|
||||||
|
ol("breadcrumb") {
|
||||||
|
path.tokens.forEach { token ->
|
||||||
|
li("breadcrumb-item") {
|
||||||
|
+token.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
configEditor(item, descriptor, default)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Element.visualPropertyEditor(
|
||||||
|
path: Name,
|
||||||
|
item: Vision,
|
||||||
|
descriptor: NodeDescriptor? = item.descriptor,
|
||||||
|
default: Meta? = null
|
||||||
|
) = render(this) {
|
||||||
|
this.visualPropertyEditor(path, item, descriptor, default)
|
||||||
|
}
|
7
ui/bootstrap/src/main/resources/css/bootstrap.min.css
vendored
Normal file
@ -1,3 +1,11 @@
|
|||||||
|
/*full height*/
|
||||||
|
html, body {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container-fluid { height: inherit; }
|
||||||
|
|
||||||
/* Remove default bullets */
|
/* Remove default bullets */
|
||||||
ul, .tree {
|
ul, .tree {
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
@ -36,5 +44,13 @@ ul, .tree {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tree-label-inactive {
|
.tree-label-inactive {
|
||||||
color: gray;
|
color: lightgrey;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-label-selected{
|
||||||
|
background-color: lightblue;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-padding{
|
||||||
|
padding: 0;
|
||||||
}
|
}
|
7
ui/bootstrap/src/main/resources/js/bootstrap.bundle.min.js
vendored
Normal file
2
ui/bootstrap/src/main/resources/js/jquery-3.4.1.min.js
vendored
Normal file
0
ui/build.gradle.kts
Normal file
21
ui/material/build.gradle.kts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
plugins {
|
||||||
|
id("scientifik.js")
|
||||||
|
}
|
||||||
|
|
||||||
|
val dataforgeVersion: String by rootProject.extra
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
target {
|
||||||
|
useCommonJs()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies{
|
||||||
|
api(project(":ui:react"))
|
||||||
|
|
||||||
|
api("subroh0508.net.kotlinmaterialui:core:0.4.0")
|
||||||
|
api("subroh0508.net.kotlinmaterialui:lab:0.4.0")
|
||||||
|
api(npm("@material-ui/core","4.9.14"))
|
||||||
|
api(npm("@material-ui/lab","4.0.0-alpha.51"))
|
||||||
|
//api(npm("@material-ui/icons","4.9.1"))
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package hep.dataforge.vision.material
|
||||||
|
|
||||||
|
//@JsModule("@material-ui/icons/ExpandMore")
|
||||||
|
//external class ExpandMoreIcon : Component<RProps, RState>{
|
||||||
|
// override fun render(): dynamic
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//@JsModule("@material-ui/icons/ChevronRight")
|
||||||
|
//external class ChevronRightIcon : Component<RProps, RState>{
|
||||||
|
// override fun render(): dynamic
|
||||||
|
//}
|
@ -0,0 +1,97 @@
|
|||||||
|
package hep.dataforge.vision.material
|
||||||
|
|
||||||
|
import hep.dataforge.vision.react.component
|
||||||
|
import hep.dataforge.vision.react.state
|
||||||
|
import kotlinx.html.DIV
|
||||||
|
import materialui.components.card.card
|
||||||
|
import materialui.components.cardcontent.cardContent
|
||||||
|
import materialui.components.cardheader.cardHeader
|
||||||
|
import materialui.components.container.container
|
||||||
|
import materialui.components.container.enums.ContainerMaxWidth
|
||||||
|
import materialui.components.expansionpanel.expansionPanel
|
||||||
|
import materialui.components.expansionpaneldetails.expansionPanelDetails
|
||||||
|
import materialui.components.expansionpanelsummary.expansionPanelSummary
|
||||||
|
import materialui.components.grid.GridElementBuilder
|
||||||
|
import materialui.components.grid.enums.GridDirection
|
||||||
|
import materialui.components.grid.enums.GridStyle
|
||||||
|
import materialui.components.grid.grid
|
||||||
|
import materialui.components.paper.paper
|
||||||
|
import materialui.components.typography.typographyH5
|
||||||
|
import react.RBuilder
|
||||||
|
import react.RProps
|
||||||
|
import react.child
|
||||||
|
import react.dom.RDOMBuilder
|
||||||
|
|
||||||
|
|
||||||
|
fun accordionComponent(elements: List<Pair<String, RDOMBuilder<DIV>.() -> Unit>>) =
|
||||||
|
component<RProps> {
|
||||||
|
val expandedIndex: Int? by state { null }
|
||||||
|
|
||||||
|
container {
|
||||||
|
attrs {
|
||||||
|
maxWidth = ContainerMaxWidth.`false`
|
||||||
|
}
|
||||||
|
elements.forEachIndexed { index, (header, body) ->
|
||||||
|
expansionPanel {
|
||||||
|
attrs {
|
||||||
|
expanded = index == expandedIndex
|
||||||
|
}
|
||||||
|
expansionPanelSummary {
|
||||||
|
typographyH5 {
|
||||||
|
+header
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expansionPanelDetails {
|
||||||
|
this.body()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typealias RAccordionBuilder = MutableList<Pair<String, RDOMBuilder<DIV>.() -> Unit>>
|
||||||
|
|
||||||
|
fun RAccordionBuilder.entry(title: String, builder: RDOMBuilder<DIV>.() -> Unit) {
|
||||||
|
add(title to builder)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun RBuilder.accordion(builder: RAccordionBuilder.() -> Unit) {
|
||||||
|
val list: List<Pair<String, RDOMBuilder<DIV>.() -> Unit>> =
|
||||||
|
ArrayList<Pair<String, RDOMBuilder<DIV>.() -> Unit>>().apply(builder)
|
||||||
|
child(accordionComponent(list))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun RBuilder.materialCard(title: String, block: RBuilder.() -> Unit) {
|
||||||
|
card {
|
||||||
|
cardHeader {
|
||||||
|
attrs {
|
||||||
|
this.title = typographyH5 {
|
||||||
|
+title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cardContent {
|
||||||
|
paper {
|
||||||
|
this.block()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun RBuilder.column(vararg classMap: Pair<GridStyle, String>, block: GridElementBuilder<DIV>.() -> Unit) =
|
||||||
|
grid(*classMap) {
|
||||||
|
attrs {
|
||||||
|
container = true
|
||||||
|
direction = GridDirection.column
|
||||||
|
}
|
||||||
|
block()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun RBuilder.row(vararg classMap: Pair<GridStyle, String>, block: GridElementBuilder<DIV>.() -> Unit) =
|
||||||
|
grid(*classMap) {
|
||||||
|
attrs {
|
||||||
|
container = true
|
||||||
|
direction = GridDirection.row
|
||||||
|
}
|
||||||
|
block()
|
||||||
|
}
|
@ -0,0 +1,94 @@
|
|||||||
|
package hep.dataforge.vision.material
|
||||||
|
|
||||||
|
import hep.dataforge.names.Name
|
||||||
|
import hep.dataforge.names.plus
|
||||||
|
import hep.dataforge.names.toName
|
||||||
|
import hep.dataforge.vision.Vision
|
||||||
|
import hep.dataforge.vision.VisionGroup
|
||||||
|
import hep.dataforge.vision.isEmpty
|
||||||
|
import hep.dataforge.vision.react.component
|
||||||
|
import hep.dataforge.vision.react.state
|
||||||
|
import kotlinx.html.UL
|
||||||
|
import materialui.lab.components.treeItem.treeItem
|
||||||
|
import materialui.lab.components.treeView.SingleSelectTreeViewElementBuilder
|
||||||
|
import materialui.lab.components.treeView.treeView
|
||||||
|
import react.FunctionalComponent
|
||||||
|
import react.RBuilder
|
||||||
|
import react.RProps
|
||||||
|
import react.child
|
||||||
|
import react.dom.span
|
||||||
|
|
||||||
|
interface ObjectTreeProps : RProps {
|
||||||
|
var name: Name
|
||||||
|
var selected: Name?
|
||||||
|
var obj: Vision
|
||||||
|
var clickCallback: (Name?) -> Unit
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun RBuilder.treeBranch(name: Name, obj: Vision): Unit {
|
||||||
|
treeItem {
|
||||||
|
val token = name.last()?.toString() ?: "World"
|
||||||
|
attrs {
|
||||||
|
nodeId = name.toString()
|
||||||
|
label {
|
||||||
|
span {
|
||||||
|
+token
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj is VisionGroup) {
|
||||||
|
obj.children.entries
|
||||||
|
.filter { !it.key.toString().startsWith("@") } // ignore statics and other hidden children
|
||||||
|
.sortedBy { (it.value as? VisionGroup)?.isEmpty ?: true }
|
||||||
|
.forEach { (childToken, child) ->
|
||||||
|
treeBranch(name + childToken, child)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val ObjectTree: FunctionalComponent<ObjectTreeProps> = component { props ->
|
||||||
|
var selected: String? by state { props.selected.toString() }
|
||||||
|
treeView {
|
||||||
|
this as SingleSelectTreeViewElementBuilder<UL>
|
||||||
|
attrs {
|
||||||
|
this.selected = selected
|
||||||
|
this.onNodeSelect{ _, selectedItem ->
|
||||||
|
selected = selectedItem
|
||||||
|
val itemName = selected?.toName()
|
||||||
|
props.clickCallback(itemName)
|
||||||
|
Unit
|
||||||
|
}
|
||||||
|
defaultCollapseIcon {
|
||||||
|
span{
|
||||||
|
+"-"
|
||||||
|
}
|
||||||
|
//child(ExpandMoreIcon::class) {}
|
||||||
|
}//{<ExpandMoreIcon />}
|
||||||
|
defaultExpandIcon {
|
||||||
|
span{
|
||||||
|
+"+"
|
||||||
|
}
|
||||||
|
//child(ChevronRightIcon::class) {}
|
||||||
|
}//{<ChevronRightIcon />}
|
||||||
|
}
|
||||||
|
treeBranch(props.name, props.obj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun RBuilder.objectTree(
|
||||||
|
vision: Vision,
|
||||||
|
selected: Name? = null,
|
||||||
|
clickCallback: (Name?) -> Unit = {}
|
||||||
|
) {
|
||||||
|
child(ObjectTree) {
|
||||||
|
attrs {
|
||||||
|
this.name = Name.EMPTY
|
||||||
|
this.obj = vision
|
||||||
|
this.selected = selected
|
||||||
|
this.clickCallback = clickCallback
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,37 @@
|
|||||||
|
package materialui.components.slider
|
||||||
|
|
||||||
|
import kotlinx.html.DIV
|
||||||
|
import kotlinx.html.Tag
|
||||||
|
import materialui.components.MaterialElementBuilder
|
||||||
|
import materialui.components.getValue
|
||||||
|
import materialui.components.inputbase.enums.InputBaseStyle
|
||||||
|
import materialui.components.setValue
|
||||||
|
import react.RClass
|
||||||
|
import react.ReactElement
|
||||||
|
|
||||||
|
class SliderElementBuilder<Props: SliderProps> internal constructor(
|
||||||
|
type: RClass<Props>,
|
||||||
|
classMap: List<Pair<Enum<*>, String>>
|
||||||
|
) : MaterialElementBuilder<DIV, Props>(type, classMap, { DIV(mapOf(), it) }) {
|
||||||
|
fun Tag.classes(vararg classMap: Pair<InputBaseStyle, String>) {
|
||||||
|
classes(classMap.toList())
|
||||||
|
}
|
||||||
|
var Tag.defaultValue: Number? by materialProps
|
||||||
|
var Tag.disabled: Boolean? by materialProps// = false
|
||||||
|
var Tag.getAriaLabel: String? by materialProps
|
||||||
|
var Tag.getAriaValueText: String? by materialProps
|
||||||
|
var Tag.marks: Array<String>? by materialProps
|
||||||
|
var Tag.max: Number? by materialProps// = 100
|
||||||
|
var Tag.min: Number? by materialProps// = 0,
|
||||||
|
var Tag.name: String? by materialProps
|
||||||
|
var Tag.onChange: ((dynamic, Number) -> Unit)? by materialProps
|
||||||
|
var Tag.onChangeCommitted: ((dynamic, Number) -> Unit)? by materialProps
|
||||||
|
var Tag.orientation: SliderOrientation? by materialProps
|
||||||
|
var Tag.scale: ((Number) -> Number)? by materialProps// {it}
|
||||||
|
var Tag.step: Number? by materialProps// = 1,
|
||||||
|
//ThumbComponent = 'span',
|
||||||
|
var Tag.track: SliderTrack by materialProps
|
||||||
|
var Tag.value: Number? by materialProps
|
||||||
|
var Tag.ValueLabelComponent: ReactElement? by materialProps
|
||||||
|
var Tag.valueLabelDisplay: SliderValueLabelDisplay by materialProps
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package materialui.components.slider
|
||||||
|
|
||||||
|
enum class SliderOrientation {
|
||||||
|
horizontal,
|
||||||
|
vertical
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class SliderTrack{
|
||||||
|
normal,
|
||||||
|
`false`,
|
||||||
|
inverted,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class SliderValueLabelDisplay{
|
||||||
|
on,
|
||||||
|
auto,
|
||||||
|
off
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
package materialui.components.slider
|
||||||
|
|
||||||
|
import materialui.components.StandardProps
|
||||||
|
import materialui.components.input.enums.InputStyle
|
||||||
|
import react.RBuilder
|
||||||
|
import react.RClass
|
||||||
|
import react.ReactElement
|
||||||
|
|
||||||
|
@JsModule("@material-ui/core/Slider")
|
||||||
|
private external val sliderModule: dynamic
|
||||||
|
|
||||||
|
external interface SliderProps : StandardProps {
|
||||||
|
var defaultValue: Number?
|
||||||
|
var disabled: Boolean?// = false
|
||||||
|
var getAriaLabel: String?
|
||||||
|
var getAriaValueText: String?
|
||||||
|
var marks: Array<String>?
|
||||||
|
var max: Number?// = 100
|
||||||
|
var min: Number?// = 0,
|
||||||
|
var name: String?
|
||||||
|
var onChange: ((dynamic, Number) -> Unit)?
|
||||||
|
var onChangeCommitted: ((dynamic, Number) -> Unit)?
|
||||||
|
var orientation: SliderOrientation?
|
||||||
|
var scale: ((Number) -> Number)?// {it}
|
||||||
|
var step: Number? // = 1,
|
||||||
|
//ThumbComponent = 'span',
|
||||||
|
var track: SliderTrack
|
||||||
|
var value: Number?
|
||||||
|
var ValueLabelComponent: ReactElement?
|
||||||
|
var valueLabelDisplay: SliderValueLabelDisplay
|
||||||
|
//valueLabelFormat = Identity,
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("UnsafeCastFromDynamic")
|
||||||
|
private val sliderComponent: RClass<SliderProps> = sliderModule.default
|
||||||
|
|
||||||
|
fun RBuilder.slider(vararg classMap: Pair<InputStyle, String>, block: SliderElementBuilder<SliderProps>.() -> Unit) =
|
||||||
|
child(SliderElementBuilder(sliderComponent, classMap.toList()).apply(block).create())
|
22
ui/react/build.gradle.kts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
plugins {
|
||||||
|
id("scientifik.js")
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
target {
|
||||||
|
useCommonJs()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val reactVersion by extra("16.13.1")
|
||||||
|
|
||||||
|
dependencies{
|
||||||
|
api(project(":visionforge-core"))
|
||||||
|
|
||||||
|
//api("org.jetbrains:kotlin-react:16.13.1-pre.104-kotlin-1.3.72")
|
||||||
|
api("org.jetbrains:kotlin-react-dom:$reactVersion-pre.104-kotlin-1.3.72")
|
||||||
|
|
||||||
|
api(npm("react", reactVersion))
|
||||||
|
api(npm("react-dom", reactVersion))
|
||||||
|
api(npm("react-is", reactVersion))
|
||||||
|
}
|
@ -0,0 +1,74 @@
|
|||||||
|
package hep.dataforge.vision.react
|
||||||
|
|
||||||
|
import kotlinx.css.*
|
||||||
|
import kotlinx.css.properties.deg
|
||||||
|
import kotlinx.css.properties.rotate
|
||||||
|
import styled.StyleSheet
|
||||||
|
|
||||||
|
object TreeStyles : StyleSheet("treeStyles", true) {
|
||||||
|
/**
|
||||||
|
* Remove default bullets
|
||||||
|
*/
|
||||||
|
val tree by css {
|
||||||
|
paddingLeft = 8.px
|
||||||
|
marginLeft = 0.px
|
||||||
|
listStyleType = ListStyleType.none
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Style the caret/arrow
|
||||||
|
*/
|
||||||
|
val treeCaret by css {
|
||||||
|
cursor = Cursor.pointer
|
||||||
|
userSelect = UserSelect.none
|
||||||
|
/* Create the caret/arrow with a unicode, and style it */
|
||||||
|
before {
|
||||||
|
content = "\u25B6".quoted
|
||||||
|
color = Color.black
|
||||||
|
display = Display.inlineBlock
|
||||||
|
marginRight = 6.px
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val treeItem by css {
|
||||||
|
alignItems = Align.center
|
||||||
|
paddingLeft = 10.px
|
||||||
|
borderLeftStyle = BorderStyle.dashed
|
||||||
|
borderLeftWidth = 1.px
|
||||||
|
borderLeftColor = Color.lightGray
|
||||||
|
}
|
||||||
|
|
||||||
|
val treeLeaf by css {
|
||||||
|
display = Display.flex
|
||||||
|
flexDirection = FlexDirection.row
|
||||||
|
userSelect = UserSelect.none
|
||||||
|
alignItems = Align.center
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rotate the caret/arrow icon when clicked on (using JavaScript)
|
||||||
|
*/
|
||||||
|
val treeCaredDown by css {
|
||||||
|
before {
|
||||||
|
content = "\u25B6".quoted
|
||||||
|
color = Color.black
|
||||||
|
display = Display.inlineBlock
|
||||||
|
marginRight = 6.px
|
||||||
|
transform.rotate(90.deg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val treeLabel by css {
|
||||||
|
overflow = Overflow.hidden
|
||||||
|
}
|
||||||
|
|
||||||
|
val treeLabelInactive by css {
|
||||||
|
color = Color.lightGray
|
||||||
|
}
|
||||||
|
|
||||||
|
val treeLabelSelected by css {
|
||||||
|
backgroundColor = Color.lightBlue
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,265 @@
|
|||||||
|
package hep.dataforge.vision.react
|
||||||
|
|
||||||
|
import hep.dataforge.meta.*
|
||||||
|
import hep.dataforge.meta.descriptors.*
|
||||||
|
import hep.dataforge.names.Name
|
||||||
|
import hep.dataforge.names.NameToken
|
||||||
|
import hep.dataforge.names.plus
|
||||||
|
import hep.dataforge.values.Value
|
||||||
|
import kotlinx.css.*
|
||||||
|
import kotlinx.css.properties.TextDecoration
|
||||||
|
import kotlinx.html.js.onClickFunction
|
||||||
|
import org.w3c.dom.Element
|
||||||
|
import org.w3c.dom.events.Event
|
||||||
|
import react.*
|
||||||
|
import react.dom.div
|
||||||
|
import react.dom.render
|
||||||
|
import styled.*
|
||||||
|
|
||||||
|
interface ConfigEditorItemProps : 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?
|
||||||
|
}
|
||||||
|
|
||||||
|
private val ConfigEditorItem: FunctionalComponent<ConfigEditorItemProps> = component { props ->
|
||||||
|
configEditorItem(props)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun RFBuilder.configEditorItem(props: ConfigEditorItemProps) {
|
||||||
|
var expanded: Boolean by state { true }
|
||||||
|
var item: MetaItem<Config>? by state { props.root[props.name] }
|
||||||
|
val descriptorItem: ItemDescriptor? = props.descriptor?.get(props.name)
|
||||||
|
val defaultItem = props.default?.get(props.name)
|
||||||
|
var actualItem: MetaItem<Meta>? by state { item ?: defaultItem ?: descriptorItem?.defaultItem() }
|
||||||
|
|
||||||
|
val token = props.name.last()?.toString() ?: "Properties"
|
||||||
|
|
||||||
|
fun update() {
|
||||||
|
item = props.root[props.name]
|
||||||
|
actualItem = item ?: defaultItem ?: descriptorItem?.defaultItem()
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffectWithCleanup(listOf(props.root)) {
|
||||||
|
props.root.onChange(this) { name, _, _ ->
|
||||||
|
if (name == props.name) {
|
||||||
|
update()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return@useEffectWithCleanup { props.root.removeListener(this) }
|
||||||
|
}
|
||||||
|
|
||||||
|
val expanderClick: (Event) -> Unit = {
|
||||||
|
expanded = !expanded
|
||||||
|
}
|
||||||
|
|
||||||
|
val valueChanged: (Value?) -> Unit = {
|
||||||
|
if (it == null) {
|
||||||
|
props.root.remove(props.name)
|
||||||
|
} else {
|
||||||
|
props.root[props.name] = it
|
||||||
|
}
|
||||||
|
update()
|
||||||
|
}
|
||||||
|
|
||||||
|
val removeClick: (Event) -> Unit = {
|
||||||
|
props.root.remove(props.name)
|
||||||
|
update()
|
||||||
|
}
|
||||||
|
|
||||||
|
when (actualItem) {
|
||||||
|
is MetaItem.NodeItem -> {
|
||||||
|
div {
|
||||||
|
styledSpan {
|
||||||
|
css {
|
||||||
|
+TreeStyles.treeCaret
|
||||||
|
if (expanded) {
|
||||||
|
+TreeStyles.treeCaredDown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
attrs {
|
||||||
|
onClickFunction = expanderClick
|
||||||
|
}
|
||||||
|
}
|
||||||
|
styledSpan {
|
||||||
|
css {
|
||||||
|
+TreeStyles.treeLabel
|
||||||
|
if (item == null) {
|
||||||
|
+TreeStyles.treeLabelInactive
|
||||||
|
}
|
||||||
|
}
|
||||||
|
+token
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (expanded) {
|
||||||
|
styledUl {
|
||||||
|
css {
|
||||||
|
+TreeStyles.tree
|
||||||
|
}
|
||||||
|
val keys = buildSet<NameToken> {
|
||||||
|
(descriptorItem as? NodeDescriptor)?.items?.keys?.forEach {
|
||||||
|
add(NameToken(it))
|
||||||
|
}
|
||||||
|
item?.node?.items?.keys?.let { addAll(it) }
|
||||||
|
defaultItem?.node?.items?.keys?.let { addAll(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
keys.forEach { token ->
|
||||||
|
styledLi {
|
||||||
|
css {
|
||||||
|
+TreeStyles.treeItem
|
||||||
|
}
|
||||||
|
child(ConfigEditorItem) {
|
||||||
|
attrs {
|
||||||
|
this.key = props.name.toString()
|
||||||
|
this.root = props.root
|
||||||
|
this.name = props.name + token
|
||||||
|
this.default = props.default
|
||||||
|
this.descriptor = props.descriptor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//configEditor(props.root, props.name + token, props.descriptor, props.default)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is MetaItem.ValueItem -> {
|
||||||
|
styledDiv {
|
||||||
|
css {
|
||||||
|
+TreeStyles.treeLeaf
|
||||||
|
justifyContent = JustifyContent.flexEnd
|
||||||
|
}
|
||||||
|
styledDiv {
|
||||||
|
css {
|
||||||
|
flexGrow = 1.0
|
||||||
|
}
|
||||||
|
styledSpan {
|
||||||
|
css {
|
||||||
|
+TreeStyles.treeLabel
|
||||||
|
if (item == null) {
|
||||||
|
+TreeStyles.treeLabelInactive
|
||||||
|
}
|
||||||
|
}
|
||||||
|
+token
|
||||||
|
}
|
||||||
|
}
|
||||||
|
styledDiv {
|
||||||
|
valueChooser(
|
||||||
|
props.name,
|
||||||
|
actualItem,
|
||||||
|
descriptorItem as? ValueDescriptor,
|
||||||
|
valueChanged
|
||||||
|
)
|
||||||
|
}
|
||||||
|
styledDiv {
|
||||||
|
css {
|
||||||
|
flexShrink = 1.0
|
||||||
|
}
|
||||||
|
styledButton {
|
||||||
|
css {
|
||||||
|
backgroundColor = Color.white
|
||||||
|
borderStyle = BorderStyle.solid
|
||||||
|
borderRadius = 2.px
|
||||||
|
padding(1.px, 5.px)
|
||||||
|
marginLeft = 4.px
|
||||||
|
textAlign = TextAlign.center
|
||||||
|
textDecoration = TextDecoration.none
|
||||||
|
display = Display.inlineBlock
|
||||||
|
cursor = Cursor.pointer
|
||||||
|
disabled {
|
||||||
|
cursor = Cursor.auto
|
||||||
|
borderStyle = BorderStyle.dashed
|
||||||
|
color = Color.lightGray
|
||||||
|
}
|
||||||
|
}
|
||||||
|
+"\u00D7"
|
||||||
|
attrs {
|
||||||
|
if (item == null) {
|
||||||
|
disabled = true
|
||||||
|
} else {
|
||||||
|
onClickFunction = removeClick
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ConfigEditorProps : RProps {
|
||||||
|
var id: Name
|
||||||
|
var root: Config
|
||||||
|
var default: Meta?
|
||||||
|
var descriptor: NodeDescriptor?
|
||||||
|
}
|
||||||
|
|
||||||
|
val ConfigEditor = component<ConfigEditorProps> { props ->
|
||||||
|
child(ConfigEditorItem) {
|
||||||
|
attrs {
|
||||||
|
this.key = ""
|
||||||
|
this.root = props.root
|
||||||
|
this.name = Name.EMPTY
|
||||||
|
this.default = props.default
|
||||||
|
this.descriptor = props.descriptor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Element.configEditor(config: Config, descriptor: NodeDescriptor? = null, default: Meta? = null, key: Any? = null) {
|
||||||
|
render(this) {
|
||||||
|
child(ConfigEditor) {
|
||||||
|
attrs {
|
||||||
|
this.key = key?.toString() ?: ""
|
||||||
|
this.root = config
|
||||||
|
this.descriptor = descriptor
|
||||||
|
this.default = default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun RBuilder.configEditor(config: Config, descriptor: NodeDescriptor? = null, default: Meta? = null, key: Any? = null) {
|
||||||
|
child(ConfigEditor) {
|
||||||
|
attrs {
|
||||||
|
this.key = key?.toString() ?: ""
|
||||||
|
this.root = config
|
||||||
|
this.descriptor = descriptor
|
||||||
|
this.default = default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun RBuilder.configEditor(
|
||||||
|
obj: Configurable,
|
||||||
|
descriptor: NodeDescriptor? = obj.descriptor,
|
||||||
|
default: Meta? = null,
|
||||||
|
key: Any? = null
|
||||||
|
) {
|
||||||
|
child(ConfigEditor) {
|
||||||
|
attrs {
|
||||||
|
this.key = key?.toString() ?: ""
|
||||||
|
this.root = obj.config
|
||||||
|
this.descriptor = descriptor
|
||||||
|
this.default = default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package hep.dataforge.vision.react
|
||||||
|
|
||||||
|
import kotlinx.css.Display
|
||||||
|
import kotlinx.css.FlexDirection
|
||||||
|
import kotlinx.css.display
|
||||||
|
import kotlinx.css.flexDirection
|
||||||
|
import kotlinx.html.DIV
|
||||||
|
import react.RBuilder
|
||||||
|
import styled.StyledDOMBuilder
|
||||||
|
import styled.css
|
||||||
|
import styled.styledDiv
|
||||||
|
|
||||||
|
inline fun RBuilder.flexColumn(block: StyledDOMBuilder<DIV>.() -> Unit) =
|
||||||
|
styledDiv {
|
||||||
|
css {
|
||||||
|
display = Display.flex
|
||||||
|
flexDirection = FlexDirection.column
|
||||||
|
}
|
||||||
|
block()
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun RBuilder.flexRow(block: StyledDOMBuilder<DIV>.() -> Unit) =
|
||||||
|
styledDiv {
|
||||||
|
css {
|
||||||
|
display = Display.flex
|
||||||
|
flexDirection = FlexDirection.row
|
||||||
|
}
|
||||||
|
block()
|
||||||
|
}
|
38
ui/react/src/main/kotlin/hep/dataforge/vision/react/react.kt
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package hep.dataforge.vision.react
|
||||||
|
|
||||||
|
import react.*
|
||||||
|
import kotlin.properties.ReadWriteProperty
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
class RFBuilder : RBuilder()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get functional component from [func]
|
||||||
|
*/
|
||||||
|
fun <P : RProps> component(
|
||||||
|
func: RFBuilder.(props: P) -> Unit
|
||||||
|
): FunctionalComponent<P> {
|
||||||
|
return { props: P ->
|
||||||
|
val nodes = RFBuilder().apply { func(props) }.childList
|
||||||
|
when (nodes.size) {
|
||||||
|
0 -> null
|
||||||
|
1 -> nodes.first()
|
||||||
|
else -> createElement(Fragment, kotlinext.js.js {}, *nodes.toTypedArray())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> RFBuilder.state(init: () -> T): ReadWriteProperty<Any?, T> =
|
||||||
|
object : ReadWriteProperty<Any?, T> {
|
||||||
|
val pair = react.useState(init)
|
||||||
|
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
|
||||||
|
return pair.first
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
|
||||||
|
pair.second(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> RFBuilder.memoize(vararg deps: dynamic, builder: () -> T): T = useMemo(builder, deps)
|
||||||
|
|
@ -0,0 +1,9 @@
|
|||||||
|
package hep.dataforge.vision.react
|
||||||
|
|
||||||
|
import styled.StyleSheet
|
||||||
|
|
||||||
|
|
||||||
|
class MainStyle: StyleSheet("main", true){
|
||||||
|
|
||||||
|
|
||||||
|
}
|