0.2.0 #71
47
README.md
47
README.md
@ -14,19 +14,11 @@
|
||||
* [Features](#features)
|
||||
* [About DataForge](#about-dataforge)
|
||||
* [Modules contained in this repository](#modules-contained-in-this-repository)
|
||||
* [visionforge-core](#visionforge-core)
|
||||
* [visionforge-fx](#visionforge-fx)
|
||||
* [visionforge-gdml](#visionforge-gdml)
|
||||
* [visionforge-markdown](#visionforge-markdown)
|
||||
* [visionforge-plotly](#visionforge-plotly)
|
||||
* [visionforge-server](#visionforge-server)
|
||||
* [visionforge-solid](#visionforge-solid)
|
||||
* [visionforge-threejs](#visionforge-threejs)
|
||||
* [Visualization for External Systems](#visualization-for-external-systems)
|
||||
* [Demonstrations](#demonstrations)
|
||||
* [Simple Example - Solid Showcase](#simple-example---solid-showcase)
|
||||
* [Full-Stack Application Example - Muon Monitor](#full-stack-application-example---muon-monitor-visualization)
|
||||
* [GDML Example](#gdml-example)
|
||||
* [Simple Example - Solid Showcase](#simple-example---solid-showcase)
|
||||
* [Full-Stack Application Example - Muon Monitor](#full-stack-application-example---muon-monitor-visualization)
|
||||
* [GDML Example](#gdml-example)
|
||||
|
||||
|
||||
## Introduction
|
||||
@ -62,39 +54,14 @@ Platform uses some of the concepts and modules of DataForge, including: `Meta`,
|
||||
`Provider`, and some others.
|
||||
|
||||
To learn more about DataForge, please consult the following URLs:
|
||||
* [Kotlin multiplatform implementation of DataForge](https://github.com/mipt-npm/dataforge-core)
|
||||
* [DataForge documentation](http://npm.mipt.ru/dataforge/)
|
||||
* [Original implementation of DataForge](https://bitbucket.org/Altavir/dataforge/src/default/)
|
||||
* [Kotlin multiplatform implementation of DataForge](https://github.com/mipt-npm/dataforge-core)
|
||||
* [DataForge documentation](http://npm.mipt.ru/dataforge/)
|
||||
* [Original implementation of DataForge](https://bitbucket.org/Altavir/dataforge/src/default/)
|
||||
|
||||
|
||||
## Modules contained in this repository
|
||||
|
||||
### visionforge-core
|
||||
|
||||
Contains a general hierarchy of classes and interfaces useful for visualization.
|
||||
This module is not specific to 3D-visualization.
|
||||
|
||||
The `visionforge-core` module also includes configuration editors for JS (in `jsMain`) and JVM (in `jvmMain`).
|
||||
|
||||
**Class diagram:**
|
||||
|
||||
![](docs/images/class-diag-core.png)
|
||||
|
||||
### visionforge-fx
|
||||
|
||||
### visionforge-gdml
|
||||
|
||||
GDML bindings for 3D visualization (to be moved to gdml project).
|
||||
|
||||
### visionforge-markdown
|
||||
|
||||
### visionforge-plotly
|
||||
|
||||
### visionforge-server
|
||||
|
||||
### visionforge-solid
|
||||
|
||||
Includes common classes and serializers for 3D visualization, as well as Three.js and JavaFX implementations.
|
||||
$modules
|
||||
|
||||
**Class diagram:**
|
||||
|
||||
|
@ -1,26 +1,23 @@
|
||||
plugins {
|
||||
id("ru.mipt.npm.gradle.project")
|
||||
id("org.jetbrains.kotlinx.kover") version "0.5.0-RC"
|
||||
}
|
||||
|
||||
val dataforgeVersion by extra("0.5.2")
|
||||
val fxVersion by extra("11")
|
||||
|
||||
allprojects {
|
||||
subprojects {
|
||||
if (name.startsWith("visionforge")) apply<MavenPublishPlugin>()
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
maven("https://repo.kotlin.link")
|
||||
mavenCentral()
|
||||
maven("https://maven.jzy3d.org/releases")
|
||||
}
|
||||
|
||||
group = "space.kscience"
|
||||
version = "0.2.0"
|
||||
}
|
||||
version = "0.2.0-dev-99"
|
||||
|
||||
subprojects {
|
||||
if (name.startsWith("visionforge")) {
|
||||
plugins.apply("maven-publish")
|
||||
}
|
||||
}
|
||||
|
||||
ksciencePublish {
|
||||
@ -30,10 +27,10 @@ ksciencePublish {
|
||||
}
|
||||
|
||||
apiValidation {
|
||||
validationDisabled = true
|
||||
ignoredPackages.add("info.laht.threekt")
|
||||
}
|
||||
|
||||
readme.readmeTemplate = file("docs/templates/README-TEMPLATE.md")
|
||||
|
||||
//workaround for https://youtrack.jetbrains.com/issue/KT-48273
|
||||
rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin::class.java) {
|
||||
|
@ -16,6 +16,7 @@ kotlin {
|
||||
jvm {
|
||||
withJava()
|
||||
}
|
||||
|
||||
js {
|
||||
useCommonJs()
|
||||
browser {
|
||||
@ -24,6 +25,7 @@ kotlin {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
commonMain {
|
||||
dependencies {
|
||||
|
@ -47,8 +47,9 @@ kotlin {
|
||||
implementation(projects.visionforgeGdml)
|
||||
implementation(projects.visionforgePlotly)
|
||||
implementation(projects.visionforgeMarkdown)
|
||||
implementation(projects.visionforgeTables)
|
||||
implementation(projects.cernRootLoader)
|
||||
implementation(projects.jupyter.jupyterBase)
|
||||
implementation(projects.jupyter)
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,6 +57,7 @@ kotlin {
|
||||
dependencies {
|
||||
implementation(projects.ui.ring)
|
||||
implementation(projects.visionforgeThreejs)
|
||||
compileOnly(npm("webpack-bundle-analyzer","4.5.0"))
|
||||
}
|
||||
}
|
||||
|
||||
@ -66,6 +68,9 @@ kotlin {
|
||||
implementation("com.github.Ricky12Awesome:json-schema-serialization:0.6.6")
|
||||
}
|
||||
}
|
||||
all {
|
||||
languageSettings.optIn("space.kscience.dataforge.misc.DFExperimental")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,10 +3,12 @@ import space.kscience.visionforge.markup.MarkupPlugin
|
||||
import space.kscience.visionforge.plotly.PlotlyPlugin
|
||||
import space.kscience.visionforge.ring.ThreeWithControlsPlugin
|
||||
import space.kscience.visionforge.runVisionClient
|
||||
import space.kscience.visionforge.tables.TableVisionJsPlugin
|
||||
|
||||
@DFExperimental
|
||||
fun main() = runVisionClient {
|
||||
plugin(ThreeWithControlsPlugin)
|
||||
plugin(PlotlyPlugin)
|
||||
plugin(MarkupPlugin)
|
||||
plugin(TableVisionJsPlugin)
|
||||
}
|
@ -1,40 +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;
|
||||
}
|
||||
|
||||
/* Rotate the caret/arrow icon when clicked on (using JavaScript) */
|
||||
.tree-caret-down::before {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
ul, .tree {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
i, .tree-caret{
|
||||
display: inline-block;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.rotate {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.tree-label-inactive {
|
||||
color: gray;
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Playground</title>
|
||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="css/common.css">
|
||||
<script type="text/javascript" src="playground.js"></script>
|
||||
</head>
|
||||
<body class="application">
|
||||
<div class="container">
|
||||
<h1>Playground</h1>
|
||||
</div>
|
||||
<div id="app"></div>
|
||||
</body>
|
||||
</html>
|
181
demo/playground/src/jvmMain/kotlin/allThingsDemo.kt
Normal file
181
demo/playground/src/jvmMain/kotlin/allThingsDemo.kt
Normal file
@ -0,0 +1,181 @@
|
||||
package space.kscience.visionforge.examples
|
||||
|
||||
import kotlinx.html.h1
|
||||
import kotlinx.html.h2
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.values.ValueType
|
||||
import space.kscience.plotly.layout
|
||||
import space.kscience.plotly.models.ScatterMode
|
||||
import space.kscience.plotly.models.TextPosition
|
||||
import space.kscience.plotly.scatter
|
||||
import space.kscience.tables.ColumnHeader
|
||||
import space.kscience.visionforge.html.ResourceLocation
|
||||
import space.kscience.visionforge.markup.markdown
|
||||
import space.kscience.visionforge.plotly.PlotlyPlugin
|
||||
import space.kscience.visionforge.plotly.plotly
|
||||
import space.kscience.visionforge.solid.*
|
||||
import space.kscience.visionforge.tables.TableVisionPlugin
|
||||
import space.kscience.visionforge.tables.columnTable
|
||||
import java.nio.file.Paths
|
||||
|
||||
fun main() {
|
||||
val context = Context {
|
||||
plugin(Solids)
|
||||
plugin(PlotlyPlugin)
|
||||
plugin(TableVisionPlugin)
|
||||
}
|
||||
|
||||
context.makeVisionFile(
|
||||
Paths.get("VisionForgeDemo.html"),
|
||||
resourceLocation = ResourceLocation.EMBED
|
||||
) {
|
||||
markdown {
|
||||
//language=markdown
|
||||
"""
|
||||
# VisionForge
|
||||
|
||||
This is a demo for current VisionForge features. This text is written in [MarkDown](https://github.com/JetBrains/markdown)
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
h2 { +"3D visualization with Three-js" }
|
||||
vision("3D") {
|
||||
solid {
|
||||
box(100, 100, 100, name = "aBox")
|
||||
}
|
||||
}
|
||||
|
||||
h2 { +"Interactive plots with Plotly" }
|
||||
vision("plot") {
|
||||
plotly {
|
||||
scatter {
|
||||
x(1, 2, 3, 4)
|
||||
y(10, 15, 13, 17)
|
||||
mode = ScatterMode.markers
|
||||
name = "Team A"
|
||||
text("A-1", "A-2", "A-3", "A-4", "A-5")
|
||||
textposition = TextPosition.`top center`
|
||||
textfont {
|
||||
family = "Raleway, sans-serif"
|
||||
}
|
||||
marker { size = 12 }
|
||||
}
|
||||
|
||||
scatter {
|
||||
x(2, 3, 4, 5)
|
||||
y(10, 15, 13, 17)
|
||||
mode = ScatterMode.lines
|
||||
name = "Team B"
|
||||
text("B-a", "B-b", "B-c", "B-d", "B-e")
|
||||
textposition = TextPosition.`bottom center`
|
||||
textfont {
|
||||
family = "Times New Roman"
|
||||
}
|
||||
marker { size = 12 }
|
||||
}
|
||||
|
||||
layout {
|
||||
title = "Data Labels Hover"
|
||||
xaxis {
|
||||
range(0.75..5.25)
|
||||
}
|
||||
legend {
|
||||
y = 0.5
|
||||
font {
|
||||
family = "Arial, sans-serif"
|
||||
size = 20
|
||||
color("grey")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
h2 { +"Interactive tables with Tabulator" }
|
||||
vision("table") {
|
||||
val x by ColumnHeader.value(ValueType.NUMBER)
|
||||
val y by ColumnHeader.value(ValueType.NUMBER)
|
||||
columnTable(
|
||||
x to listOf(2, 3, 4, 5),
|
||||
y to listOf(10, 15, 13, 17)
|
||||
)
|
||||
}
|
||||
markdown {
|
||||
//language=markdown
|
||||
"""
|
||||
## The code for everything above
|
||||
```kotlin
|
||||
markdown {
|
||||
//language=markdown
|
||||
""${'"'}
|
||||
# VisionForge
|
||||
|
||||
This is a demo for current VisionForge features. This text is written in [MarkDown](https://github.com/JetBrains/markdown)
|
||||
""${'"'}.trimIndent()
|
||||
}
|
||||
|
||||
h2 { +"3D visualization with Three-js" }
|
||||
vision("3D") {
|
||||
solid {
|
||||
box(100, 100, 100, name = "aBox")
|
||||
}
|
||||
}
|
||||
|
||||
h2 { +"Interactive plots with Plotly" }
|
||||
vision("plot") {
|
||||
plotly {
|
||||
scatter {
|
||||
x(1, 2, 3, 4)
|
||||
y(10, 15, 13, 17)
|
||||
mode = ScatterMode.markers
|
||||
name = "Team A"
|
||||
text("A-1", "A-2", "A-3", "A-4", "A-5")
|
||||
textposition = TextPosition.`top center`
|
||||
textfont {
|
||||
family = "Raleway, sans-serif"
|
||||
}
|
||||
marker { size = 12 }
|
||||
}
|
||||
|
||||
scatter {
|
||||
x(2, 3, 4, 5)
|
||||
y(10, 15, 13, 17)
|
||||
mode = ScatterMode.lines
|
||||
name = "Team B"
|
||||
text("B-a", "B-b", "B-c", "B-d", "B-e")
|
||||
textposition = TextPosition.`bottom center`
|
||||
textfont {
|
||||
family = "Times New Roman"
|
||||
}
|
||||
marker { size = 12 }
|
||||
}
|
||||
|
||||
layout {
|
||||
title = "Data Labels Hover"
|
||||
xaxis {
|
||||
range(0.75..5.25)
|
||||
}
|
||||
legend {
|
||||
y = 0.5
|
||||
font {
|
||||
family = "Arial, sans-serif"
|
||||
size = 20
|
||||
color("grey")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
h2 { +"Interactive tables with Tabulator" }
|
||||
vision("table") {
|
||||
val x by ColumnHeader.value(ValueType.NUMBER)
|
||||
val y by ColumnHeader.value(ValueType.NUMBER)
|
||||
columnTable(
|
||||
x to listOf(2, 3, 4, 5),
|
||||
y to listOf(10, 15, 13, 17)
|
||||
)
|
||||
}
|
||||
```
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
}
|
@ -1,91 +0,0 @@
|
||||
package space.kscience.visionforge.examples
|
||||
|
||||
import kotlinx.html.div
|
||||
import kotlinx.html.h1
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.plotly.layout
|
||||
import space.kscience.plotly.models.ScatterMode
|
||||
import space.kscience.plotly.models.TextPosition
|
||||
import space.kscience.plotly.scatter
|
||||
import space.kscience.visionforge.html.ResourceLocation
|
||||
import space.kscience.visionforge.markup.markdown
|
||||
import space.kscience.visionforge.plotly.PlotlyPlugin
|
||||
import space.kscience.visionforge.plotly.plotly
|
||||
import space.kscience.visionforge.solid.*
|
||||
|
||||
fun main() {
|
||||
val context = Context {
|
||||
plugin(Solids)
|
||||
plugin(PlotlyPlugin)
|
||||
}
|
||||
|
||||
context.makeVisionFile(resourceLocation = ResourceLocation.SYSTEM) {
|
||||
markdown {
|
||||
//language=markdown
|
||||
"""
|
||||
# Section
|
||||
|
||||
**TBD**
|
||||
|
||||
## Subsection
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
div {
|
||||
h1 { +"Canvas" }
|
||||
vision("canvas") {
|
||||
solid {
|
||||
box(100, 100, 100)
|
||||
material {
|
||||
emissiveColor("red")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vision("plot") {
|
||||
plotly {
|
||||
scatter {
|
||||
x(1, 2, 3, 4)
|
||||
y(10, 15, 13, 17)
|
||||
mode = ScatterMode.markers
|
||||
name = "Team A"
|
||||
text("A-1", "A-2", "A-3", "A-4", "A-5")
|
||||
textposition = TextPosition.`top center`
|
||||
textfont {
|
||||
family = "Raleway, sans-serif"
|
||||
}
|
||||
marker { size = 12 }
|
||||
}
|
||||
|
||||
scatter {
|
||||
x(2, 3, 4, 5)
|
||||
y(10, 15, 13, 17)
|
||||
mode = ScatterMode.lines
|
||||
name = "Team B"
|
||||
text("B-a", "B-b", "B-c", "B-d", "B-e")
|
||||
textposition = TextPosition.`bottom center`
|
||||
textfont {
|
||||
family = "Times New Roman"
|
||||
}
|
||||
marker { size = 12 }
|
||||
}
|
||||
|
||||
layout {
|
||||
title = "Data Labels Hover"
|
||||
xaxis {
|
||||
range(0.75..5.25)
|
||||
}
|
||||
legend {
|
||||
y = 0.5
|
||||
font {
|
||||
family = "Arial, sans-serif"
|
||||
size = 20
|
||||
color("grey")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -24,10 +24,13 @@ public fun Context.makeVisionFile(
|
||||
title: String = "VisionForge page",
|
||||
resourceLocation: ResourceLocation = ResourceLocation.SYSTEM,
|
||||
show: Boolean = true,
|
||||
content: VisionTagConsumer<*>.() -> Unit
|
||||
content: VisionTagConsumer<*>.() -> Unit,
|
||||
): Unit {
|
||||
val actualPath = visionManager.page(title, content = content).makeFile(path) { actualPath ->
|
||||
mapOf("playground" to scriptHeader("js/visionforge-playground.js", resourceLocation, actualPath))
|
||||
mapOf(
|
||||
"playground" to scriptHeader("js/visionforge-playground.js", resourceLocation, actualPath),
|
||||
//"tables" to tabulatorCssHader
|
||||
)
|
||||
}
|
||||
if (show) Desktop.getDesktop().browse(actualPath.toFile().toURI())
|
||||
}
|
||||
|
28
demo/playground/src/jvmMain/kotlin/tables.kt
Normal file
28
demo/playground/src/jvmMain/kotlin/tables.kt
Normal file
@ -0,0 +1,28 @@
|
||||
package space.kscience.visionforge.examples
|
||||
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.values.ValueType
|
||||
import space.kscience.tables.ColumnHeader
|
||||
import space.kscience.tables.valueRow
|
||||
import space.kscience.visionforge.html.ResourceLocation
|
||||
import space.kscience.visionforge.tables.TableVisionPlugin
|
||||
import space.kscience.visionforge.tables.table
|
||||
import kotlin.math.pow
|
||||
|
||||
fun main() {
|
||||
val context = Context {
|
||||
plugin(TableVisionPlugin)
|
||||
}
|
||||
val x by ColumnHeader.value(ValueType.NUMBER)
|
||||
val y by ColumnHeader.value(ValueType.NUMBER)
|
||||
|
||||
context.makeVisionFile(resourceLocation = ResourceLocation.SYSTEM) {
|
||||
vision {
|
||||
table(x, y) {
|
||||
repeat(100) {
|
||||
valueRow(x to it, y to it.toDouble().pow(2))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
19
demo/playground/webpack.config.d/01.ring.js
vendored
19
demo/playground/webpack.config.d/01.ring.js
vendored
@ -1,3 +1,22 @@
|
||||
const ringConfig = require('@jetbrains/ring-ui/webpack.config').config;
|
||||
|
||||
config.module.rules.push(...ringConfig.module.rules)
|
||||
|
||||
config.module.rules.push(
|
||||
{
|
||||
test: /\.css$/,
|
||||
exclude: [
|
||||
'D:\\Work\\Projects\\visionforge\\build\\js\\node_modules\\@jetbrains\\ring-ui'
|
||||
],
|
||||
use: [
|
||||
{
|
||||
loader: 'style-loader',
|
||||
options: {}
|
||||
},
|
||||
{
|
||||
loader: 'css-loader',
|
||||
options: {}
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
10
demo/playground/webpack.config.d/02.bundle.js
vendored
Normal file
10
demo/playground/webpack.config.d/02.bundle.js
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
|
||||
|
||||
module.exports = {
|
||||
plugins: [
|
||||
new BundleAnalyzerPlugin({
|
||||
analyzerMode: "static",
|
||||
reportFilename: "bundle-report.html"
|
||||
})
|
||||
]
|
||||
}
|
26
docs/templates/ARTIFACT-TEMPLATE.md
vendored
Normal file
26
docs/templates/ARTIFACT-TEMPLATE.md
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
## Artifact:
|
||||
|
||||
The Maven coordinates of this project are `${group}:${name}:${version}`.
|
||||
|
||||
**Gradle Groovy:**
|
||||
```gradle
|
||||
repositories {
|
||||
maven { url 'https://repo.kotlin.link' }
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation '${group}:${name}:${version}'
|
||||
}
|
||||
```
|
||||
**Gradle Kotlin DSL:**
|
||||
```kotlin
|
||||
repositories {
|
||||
maven("https://repo.kotlin.link")
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("${group}:${name}:${version}")
|
||||
}
|
||||
```
|
138
docs/templates/README-TEMPLATE.md
vendored
Normal file
138
docs/templates/README-TEMPLATE.md
vendored
Normal file
@ -0,0 +1,138 @@
|
||||
[![JetBrains Research](https://jb.gg/badges/research.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub)
|
||||
[![DOI](https://zenodo.org/badge/174502624.svg)](https://zenodo.org/badge/latestdoi/174502624)
|
||||
|
||||
![Gradle build](https://github.com/mipt-npm/visionforge/workflows/Gradle%20build/badge.svg)
|
||||
|
||||
[![Slack](https://img.shields.io/badge/slack-channel-green?logo=slack)](https://kotlinlang.slack.com/archives/CEXV2QWNM)
|
||||
|
||||
# DataForge Visualization Platform
|
||||
|
||||
## Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Requirements](#requirements)
|
||||
* [Features](#features)
|
||||
* [About DataForge](#about-dataforge)
|
||||
* [Modules contained in this repository](#modules-contained-in-this-repository)
|
||||
* [Visualization for External Systems](#visualization-for-external-systems)
|
||||
* [Demonstrations](#demonstrations)
|
||||
* [Simple Example - Solid Showcase](#simple-example---solid-showcase)
|
||||
* [Full-Stack Application Example - Muon Monitor](#full-stack-application-example---muon-monitor-visualization)
|
||||
* [GDML Example](#gdml-example)
|
||||
|
||||
|
||||
## Introduction
|
||||
|
||||
This repository contains a [DataForge](#about-dataforge)\-based framework
|
||||
used for visualization in various scientific applications.
|
||||
|
||||
The main framework's use case for now is 3D visualization for particle physics experiments.
|
||||
Other applications including 2D plots are planned for the future.
|
||||
|
||||
The project is developed as a [Kotlin multiplatform](https://kotlinlang.org/docs/reference/multiplatform.html)
|
||||
application, currently targeting browser JavaScript and JVM.
|
||||
|
||||
## Requirements
|
||||
|
||||
JVM backend requires JDK 11 or later
|
||||
|
||||
## Features
|
||||
|
||||
The main framework's features for now include:
|
||||
- 3D visualization of complex experimental set-ups
|
||||
- Event display such as particle tracks, etc.
|
||||
- Scales up to few hundred thousands of elements
|
||||
- Camera move, rotate, zoom-in and zoom-out
|
||||
- Scene graph as an object tree with property editor
|
||||
- Settings export and import
|
||||
- Multiple platform support
|
||||
|
||||
## About DataForge
|
||||
|
||||
DataForge is a software framework for automated scientific data processing. DataForge Visualization
|
||||
Platform uses some of the concepts and modules of DataForge, including: `Meta`, `Configuration`, `Context`,
|
||||
`Provider`, and some others.
|
||||
|
||||
To learn more about DataForge, please consult the following URLs:
|
||||
* [Kotlin multiplatform implementation of DataForge](https://github.com/mipt-npm/dataforge-core)
|
||||
* [DataForge documentation](http://npm.mipt.ru/dataforge/)
|
||||
* [Original implementation of DataForge](https://bitbucket.org/Altavir/dataforge/src/default/)
|
||||
|
||||
|
||||
## Modules contained in this repository
|
||||
|
||||
$modules
|
||||
|
||||
**Class diagram:**
|
||||
|
||||
![](docs/images/class-diag-solid.png)
|
||||
|
||||
##### Prototypes
|
||||
|
||||
One of the important features of the framework is support for 3D object prototypes (sometimes
|
||||
also referred to as templates). The idea is that prototype geometry can be rendered once and reused
|
||||
for multiple objects. This helps to significantly decrease memory usage.
|
||||
|
||||
The `prototypes` property tree is defined in `SolidGroup` class via `PrototypeHolder` interface, and
|
||||
`SolidReference` class helps to reuse a template object.
|
||||
|
||||
##### Styles
|
||||
|
||||
`VisionGroup` has a `styleSheet` property that can optionally define styles at the Group's
|
||||
level. Styles are applied to child (descendant) objects using `Vision.styles: List<String>` property.
|
||||
|
||||
### visionforge-threejs
|
||||
|
||||
## Visualization for External Systems
|
||||
|
||||
The `visionforge` framework can be used to visualize geometry and events from external,
|
||||
non-Kotlin based systems, such as ROOT. This will require a plugin to convert data model
|
||||
of the external system to that of `visionforge`. Performing such integration is a work
|
||||
currently in progress.
|
||||
|
||||
|
||||
## Demonstrations
|
||||
|
||||
The `demo` module contains several 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.
|
||||
|
||||
### Simple Example - Solid 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.
|
||||
|
||||
[More details](demo/solid-showcase/README.md)
|
||||
|
||||
**Example view:**
|
||||
|
||||
![](docs/images/solid-showcase.png)
|
||||
|
||||
|
||||
### Full-Stack Application Example - Muon Monitor Visualization
|
||||
|
||||
A full-stack application example, showing the
|
||||
[Muon Monitor](http://npm.mipt.ru/en/projects/physics#mounMonitor) experiment set-up.
|
||||
|
||||
[More details](demo/muon-monitor/README.md)
|
||||
|
||||
**Example view:**
|
||||
|
||||
![](docs/images/muon-monitor.png)
|
||||
|
||||
|
||||
### GDML Example
|
||||
|
||||
Visualization example for geometry defined as GDML file.
|
||||
|
||||
[More details](demo/gdml/README.md)
|
||||
|
||||
##### Example view:
|
||||
|
||||
![](docs/images/gdml-demo.png)
|
||||
|
||||
|
||||
## Thanks and references
|
||||
The original three.js bindings were made by [Lars Ivar Hatledal](https://github.com/markaren), but the project is discontinued right now.
|
||||
|
||||
All other libraries are explicitly shown as dependencies. We would like to express specific thanks to JetBrains Kotlin-JS team for consulting us during the work.
|
@ -33,7 +33,7 @@ kotlin {
|
||||
commonMain {
|
||||
dependencies {
|
||||
implementation(projects.visionforgeSolid)
|
||||
implementation(projects.jupyter.jupyterBase)
|
||||
implementation(projects.jupyter)
|
||||
}
|
||||
}
|
||||
jvmMain {
|
||||
|
@ -1,9 +1,14 @@
|
||||
rootProject.name = "visionforge"
|
||||
|
||||
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
|
||||
enableFeaturePreview("VERSION_CATALOGS")
|
||||
|
||||
pluginManagement {
|
||||
|
||||
val toolsVersion: String by extra
|
||||
|
||||
repositories {
|
||||
//mavenLocal()
|
||||
mavenLocal()
|
||||
maven("https://repo.kotlin.link")
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
@ -17,16 +22,12 @@ pluginManagement {
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = "visionforge"
|
||||
|
||||
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
|
||||
enableFeaturePreview("VERSION_CATALOGS")
|
||||
|
||||
dependencyResolutionManagement {
|
||||
|
||||
val toolsVersion: String by extra
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
maven("https://repo.kotlin.link")
|
||||
mavenCentral()
|
||||
}
|
||||
@ -53,6 +54,7 @@ include(
|
||||
":cern-root-loader",
|
||||
":visionforge-server",
|
||||
":visionforge-plotly",
|
||||
":visionforge-tables",
|
||||
":visionforge-markdown",
|
||||
":demo:solid-showcase",
|
||||
":demo:gdml",
|
||||
@ -61,6 +63,6 @@ include(
|
||||
":demo:playground",
|
||||
":demo:plotly-fx",
|
||||
":demo:js-playground",
|
||||
":jupyter:jupyter-base",
|
||||
":jupyter",
|
||||
":jupyter:visionforge-jupyter-gdml"
|
||||
)
|
||||
|
@ -82,8 +82,10 @@ public val ThreeCanvasWithControls: FC<ThreeCanvasWithControlsProps> = fc("Three
|
||||
|
||||
useEffect {
|
||||
props.context.launch {
|
||||
solid = props.builderOfSolid.await().also {
|
||||
it?.setAsRoot(props.context.visionManager)
|
||||
solid = props.builderOfSolid.await()
|
||||
//ensure that the solid is properly rooted
|
||||
if(solid?.parent == null){
|
||||
solid?.setAsRoot(props.context.visionManager)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,12 +4,6 @@ plugins {
|
||||
|
||||
val dataforgeVersion: String by rootProject.extra
|
||||
|
||||
kscience{
|
||||
useSerialization{
|
||||
json()
|
||||
}
|
||||
}
|
||||
|
||||
kotlin {
|
||||
sourceSets {
|
||||
commonMain {
|
||||
@ -26,3 +20,13 @@ kotlin {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kscience{
|
||||
useSerialization{
|
||||
json()
|
||||
}
|
||||
}
|
||||
|
||||
readme{
|
||||
maturity = ru.mipt.npm.gradle.Maturity.DEVELOPMENT
|
||||
}
|
@ -163,6 +163,6 @@ internal class RootVisionGroup(override val manager: VisionManager) : VisionGrou
|
||||
* Designate this [VisionGroup] as a root and assign a [VisionManager] as its parent
|
||||
*/
|
||||
public fun Vision.setAsRoot(manager: VisionManager) {
|
||||
if (parent != null) error("This Vision already has a parent. It could not be set as root")
|
||||
if (parent != null) error("Vision $this already has a parent. It could not be set as root")
|
||||
parent = RootVisionGroup(manager)
|
||||
}
|
@ -78,6 +78,10 @@ public abstract class VisionTagConsumer<R>(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a vision in this HTML.
|
||||
* TODO replace by multi-receiver
|
||||
*/
|
||||
@OptIn(DFExperimental::class)
|
||||
public inline fun <T> TagConsumer<T>.vision(
|
||||
name: Name,
|
||||
@ -122,6 +126,8 @@ public abstract class VisionTagConsumer<R>(
|
||||
public const val OUTPUT_FETCH_ATTRIBUTE: String = "data-output-fetch"
|
||||
public const val OUTPUT_CONNECT_ATTRIBUTE: String = "data-output-connect"
|
||||
|
||||
public const val OUTPUT_RENDERED: String = "data-output-rendered"
|
||||
|
||||
public const val OUTPUT_NAME_ATTRIBUTE: String = "data-output-name"
|
||||
public const val OUTPUT_ENDPOINT_ATTRIBUTE: String = "data-output-endpoint"
|
||||
public const val DEFAULT_ENDPOINT: String = "."
|
||||
|
@ -19,6 +19,7 @@ import space.kscience.visionforge.html.VisionTagConsumer.Companion.OUTPUT_CONNEC
|
||||
import space.kscience.visionforge.html.VisionTagConsumer.Companion.OUTPUT_ENDPOINT_ATTRIBUTE
|
||||
import space.kscience.visionforge.html.VisionTagConsumer.Companion.OUTPUT_FETCH_ATTRIBUTE
|
||||
import space.kscience.visionforge.html.VisionTagConsumer.Companion.OUTPUT_NAME_ATTRIBUTE
|
||||
import space.kscience.visionforge.html.VisionTagConsumer.Companion.OUTPUT_RENDERED
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
|
||||
@ -64,7 +65,8 @@ public class VisionClient : AbstractPlugin() {
|
||||
|
||||
private fun renderVision(name: String, element: Element, vision: Vision?, outputMeta: Meta) {
|
||||
if (vision != null) {
|
||||
val renderer = findRendererFor(vision) ?: error("Could nof find renderer for $vision")
|
||||
val renderer = findRendererFor(vision)
|
||||
?: error("Could not find renderer for ${visionManager.encodeToString(vision)}")
|
||||
renderer.render(element, vision, outputMeta)
|
||||
|
||||
element.attributes[OUTPUT_CONNECT_ATTRIBUTE]?.let { attr ->
|
||||
@ -138,10 +140,15 @@ public class VisionClient : AbstractPlugin() {
|
||||
* Fetch from server and render a vision, described in a given with [VisionTagConsumer.OUTPUT_CLASS] class.
|
||||
*/
|
||||
public fun renderVisionIn(element: Element) {
|
||||
val name = resolveName(element) ?: error("The element is not a vision output")
|
||||
logger.info { "Found DF output with name $name" }
|
||||
if (!element.classList.contains(VisionTagConsumer.OUTPUT_CLASS)) error("The element $element is not an output element")
|
||||
val name = resolveName(element) ?: error("The element is not a vision output")
|
||||
|
||||
if (element.attributes[OUTPUT_RENDERED]?.value == "true") {
|
||||
logger.info { "VF output in element $element is already rendered" }
|
||||
return
|
||||
} else {
|
||||
logger.info { "Rendering VF output with name $name" }
|
||||
}
|
||||
|
||||
val outputMeta = element.getEmbeddedData(VisionTagConsumer.OUTPUT_META_CLASS)?.let {
|
||||
VisionManager.defaultJson.decodeFromString(MetaSerializer, it)
|
||||
@ -186,6 +193,7 @@ public class VisionClient : AbstractPlugin() {
|
||||
}
|
||||
else -> error("No embedded vision data / fetch url for $name")
|
||||
}
|
||||
element.setAttribute(OUTPUT_RENDERED, "true")
|
||||
}
|
||||
|
||||
override fun content(target: String): Map<Name, Any> = if (target == ElementVisionRenderer.TYPE) mapOf(
|
||||
@ -254,5 +262,5 @@ public fun runVisionClient(contextBuilder: ContextBuilder.() -> Unit) {
|
||||
val visionClient = context.fetch(VisionClient)
|
||||
window.asDynamic()[RENDER_FUNCTION_NAME] = visionClient::renderAllVisionsById
|
||||
|
||||
visionClient.renderAllVisions()
|
||||
//visionClient.renderAllVisions()
|
||||
}
|
@ -20,3 +20,7 @@ dependencies {
|
||||
exclude(module = "slf4j-simple")
|
||||
}
|
||||
}
|
||||
|
||||
readme{
|
||||
maturity = ru.mipt.npm.gradle.Maturity.PROTOTYPE
|
||||
}
|
@ -31,20 +31,16 @@ public actual class PlotlyPlugin : VisionPlugin(), ElementVisionRenderer {
|
||||
override fun render(element: Element, vision: Vision, meta: Meta) {
|
||||
val plot = (vision as? VisionOfPlotly)?.plot ?: error("VisionOfPlotly expected but ${vision::class} found")
|
||||
val config = PlotlyConfig.read(meta)
|
||||
// println(plot.meta)
|
||||
// println(plot.data[0].toMeta())
|
||||
element.plot(plot, config)
|
||||
}
|
||||
|
||||
override fun content(target: String): Map<Name, Any> {
|
||||
return when (target) {
|
||||
ElementVisionRenderer.TYPE -> mapOf("plotly".asName() to this)
|
||||
else -> super.content(target)
|
||||
}
|
||||
override fun content(target: String): Map<Name, Any> = when (target) {
|
||||
ElementVisionRenderer.TYPE -> mapOf("plotly".asName() to this)
|
||||
else -> super.content(target)
|
||||
}
|
||||
|
||||
public actual companion object : PluginFactory<PlotlyPlugin> {
|
||||
override val tag: PluginTag = PluginTag("vision.plotly", PluginTag.DATAFORGE_GROUP)
|
||||
override val tag: PluginTag = PluginTag("vision.plotly.js", PluginTag.DATAFORGE_GROUP)
|
||||
override val type: KClass<PlotlyPlugin> = PlotlyPlugin::class
|
||||
override fun invoke(meta: Meta, context: Context): PlotlyPlugin = PlotlyPlugin()
|
||||
}
|
||||
|
@ -1,22 +0,0 @@
|
||||
package space.kscience.visionforge.plotly
|
||||
|
||||
//internal val plotlyScriptLocation = "js/visionforge-three.js"
|
||||
//
|
||||
///**
|
||||
// * A header that stores/embeds plotly bundle and registers plotly renderer in the frontend
|
||||
// */
|
||||
//@OptIn(DFExperimental::class)
|
||||
//public fun plotlyHeader(location: ResourceLocation, filePath: Path? = null): HtmlFragment = {
|
||||
// scriptHeader(
|
||||
// plotlyScriptLocation,
|
||||
// resourceLocation = location,
|
||||
// htmlPath = filePath
|
||||
// ).invoke(this)
|
||||
// script {
|
||||
// type = "text/javascript"
|
||||
// unsafe {
|
||||
// //language=JavaScript
|
||||
// +"space.kscience.visionforge.plotly.loadPlotly()"
|
||||
// }
|
||||
// }
|
||||
//}
|
@ -9,7 +9,7 @@ import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.visionforge.VisionPlugin
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
public actual class PlotlyPlugin : VisionPlugin(), Plugin {
|
||||
public actual class PlotlyPlugin : VisionPlugin() {
|
||||
|
||||
override val tag: PluginTag get() = Companion.tag
|
||||
|
||||
|
@ -3,7 +3,9 @@ plugins {
|
||||
}
|
||||
|
||||
kscience{
|
||||
useSerialization()
|
||||
useSerialization{
|
||||
json()
|
||||
}
|
||||
}
|
||||
|
||||
kotlin {
|
||||
@ -15,3 +17,7 @@ kotlin {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
readme{
|
||||
maturity = ru.mipt.npm.gradle.Maturity.DEVELOPMENT
|
||||
}
|
40
visionforge-tables/build.gradle.kts
Normal file
40
visionforge-tables/build.gradle.kts
Normal file
@ -0,0 +1,40 @@
|
||||
plugins {
|
||||
id("ru.mipt.npm.gradle.mpp")
|
||||
}
|
||||
|
||||
val tablesVersion = "0.1.4"
|
||||
|
||||
kscience {
|
||||
useSerialization()
|
||||
}
|
||||
|
||||
kotlin {
|
||||
js {
|
||||
useCommonJs()
|
||||
binaries.library()
|
||||
browser{
|
||||
commonWebpackConfig{
|
||||
cssSupport.enabled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
commonMain {
|
||||
dependencies {
|
||||
api(project(":visionforge-core"))
|
||||
api("space.kscience:tables-kt:${tablesVersion}")
|
||||
}
|
||||
}
|
||||
jsMain {
|
||||
dependencies {
|
||||
implementation(npm("tabulator-tables", "5.0.1"))
|
||||
implementation(npm("@types/tabulator-tables", "5.0.1"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
readme{
|
||||
maturity = ru.mipt.npm.gradle.Maturity.PROTOTYPE
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package space.kscience.visionforge.tables
|
||||
|
||||
import kotlinx.serialization.modules.SerializersModule
|
||||
import kotlinx.serialization.modules.polymorphic
|
||||
import kotlinx.serialization.modules.subclass
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.context.PluginFactory
|
||||
import space.kscience.dataforge.context.PluginTag
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.visionforge.Vision
|
||||
import space.kscience.visionforge.VisionManager
|
||||
import space.kscience.visionforge.VisionPlugin
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
public class TableVisionPlugin : VisionPlugin() {
|
||||
override val tag: PluginTag get() = Companion.tag
|
||||
|
||||
override val visionSerializersModule: SerializersModule
|
||||
get() = SerializersModule {
|
||||
polymorphic(Vision::class) {
|
||||
subclass(VisionOfTable.serializer())
|
||||
}
|
||||
}
|
||||
|
||||
public companion object : PluginFactory<TableVisionPlugin> {
|
||||
override val tag: PluginTag = PluginTag("vision.table", PluginTag.DATAFORGE_GROUP)
|
||||
override val type: KClass<TableVisionPlugin> = TableVisionPlugin::class
|
||||
override fun invoke(meta: Meta, context: Context): TableVisionPlugin = TableVisionPlugin()
|
||||
}
|
||||
}
|
@ -0,0 +1,113 @@
|
||||
package space.kscience.visionforge.tables
|
||||
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
import space.kscience.dataforge.meta.*
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.dataforge.values.Null
|
||||
import space.kscience.dataforge.values.Value
|
||||
import space.kscience.dataforge.values.asValue
|
||||
import space.kscience.tables.*
|
||||
import space.kscience.visionforge.VisionBase
|
||||
import space.kscience.visionforge.html.VisionOutput
|
||||
import kotlin.jvm.JvmName
|
||||
import kotlin.reflect.typeOf
|
||||
|
||||
internal object ColumnHeaderSerializer : KSerializer<ColumnHeader<Value>> {
|
||||
|
||||
override val descriptor: SerialDescriptor get() = MetaSerializer.descriptor
|
||||
|
||||
override fun deserialize(decoder: Decoder): ColumnHeader<Value> {
|
||||
val meta = decoder.decodeSerializableValue(MetaSerializer)
|
||||
return SimpleColumnHeader(meta["name"].string!!, typeOf<Value>(), meta["meta"] ?: Meta.EMPTY)
|
||||
}
|
||||
|
||||
override fun serialize(encoder: Encoder, value: ColumnHeader<Value>) {
|
||||
val meta = Meta {
|
||||
"name" put value.name
|
||||
"meta" put value.meta
|
||||
}
|
||||
encoder.encodeSerializableValue(MetaSerializer, meta)
|
||||
}
|
||||
}
|
||||
|
||||
public val ColumnHeader<Value>.properties: ValueColumnScheme get() = ValueColumnScheme.read(meta)
|
||||
|
||||
@Serializable
|
||||
@SerialName("vision.table")
|
||||
public class VisionOfTable(
|
||||
override val headers: List<@Serializable(ColumnHeaderSerializer::class) ColumnHeader<Value>>,
|
||||
) : VisionBase(), Rows<Value> {
|
||||
|
||||
public var data: List<Meta>
|
||||
get() = meta.getIndexed("rows").entries.sortedBy { it.key?.toInt() }.map { it.value }
|
||||
set(value) {
|
||||
meta["rows"] = value
|
||||
}
|
||||
|
||||
public val rows: List<MetaRow> get() = data.map(::MetaRow)
|
||||
|
||||
override fun rowSequence(): Sequence<Row<Value>> = rows.asSequence()
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a table to a serializable vision
|
||||
*/
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
public fun <T> Table<T>.toVision(
|
||||
converter: (T?) -> Value,
|
||||
): VisionOfTable = VisionOfTable(headers as TableHeader<Value>).also { vision ->
|
||||
vision.data = rows.map { row ->
|
||||
if (row is MetaRow) {
|
||||
row.meta
|
||||
} else {
|
||||
Meta {
|
||||
headers.forEach {
|
||||
it.name put converter(row[it.name])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@JvmName("valueTableToVision")
|
||||
public fun Table<Value>.toVision(): VisionOfTable = toVision { it ?: Null }
|
||||
|
||||
@JvmName("stringTableToVision")
|
||||
public fun Table<String>.toVision(): VisionOfTable = toVision { (it ?: "").asValue() }
|
||||
|
||||
@JvmName("numberTableToVision")
|
||||
public fun Table<Number>.toVision(): VisionOfTable = toVision { (it ?: Double.NaN).asValue() }
|
||||
|
||||
@DFExperimental
|
||||
public inline fun VisionOutput.table(
|
||||
vararg headers: ColumnHeader<Value>,
|
||||
block: MutableRowTable<Value>.() -> Unit,
|
||||
): VisionOfTable = RowTable(*headers, block = block).toVision()
|
||||
|
||||
@DFExperimental
|
||||
public inline fun VisionOutput.columnTable(
|
||||
columnSize: UInt,
|
||||
block: MutableColumnTable<Value>.() -> Unit,
|
||||
): VisionOfTable = ColumnTable(columnSize, block).toVision()
|
||||
|
||||
@DFExperimental
|
||||
public fun VisionOutput.columnTable(
|
||||
vararg dataAndHeaders: Pair<ColumnHeader<Value>, List<Any?>>,
|
||||
): VisionOfTable {
|
||||
val columns = dataAndHeaders.map { (header, data) ->
|
||||
ListColumn(header, data.map { Value.of(it) })
|
||||
}
|
||||
return ColumnTable(columns).toVision()
|
||||
}
|
||||
|
||||
//public val tabulatorCssHader: HtmlFragment = {
|
||||
// link {
|
||||
// href = "https://unpkg.com/tabulator-tables@5.0.10/dist/css/tabulator.min.css"
|
||||
// rel = "stylesheet"
|
||||
// }
|
||||
//}
|
@ -0,0 +1,32 @@
|
||||
package space.kscience.visionforge.tables
|
||||
|
||||
import space.kscience.dataforge.values.Value
|
||||
import space.kscience.dataforge.values.asValue
|
||||
import space.kscience.dataforge.values.double
|
||||
import space.kscience.dataforge.values.int
|
||||
import space.kscience.tables.ColumnHeader
|
||||
import space.kscience.tables.ColumnTable
|
||||
import space.kscience.tables.get
|
||||
import kotlin.math.pow
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
internal class VisionOfTableTest {
|
||||
@Test
|
||||
fun tableSerialization() {
|
||||
val x by ColumnHeader.typed<Value>()
|
||||
val y by ColumnHeader.typed<Value>()
|
||||
|
||||
val table = ColumnTable<Value>(100U) {
|
||||
x.fill { it.asValue() }
|
||||
y.values = x.values.map { it?.double?.pow(2)?.asValue() }
|
||||
}
|
||||
|
||||
val vision = table.toVision()
|
||||
//println(Json.encodeToString(VisionOfTable.serializer(), table.toVision()))
|
||||
|
||||
val rows = vision.rowSequence().toList()
|
||||
|
||||
assertEquals(50, rows[50][x]?.int)
|
||||
}
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
package space.kscience.visionforge.tables
|
||||
|
||||
import kotlinext.js.jso
|
||||
import org.w3c.dom.Element
|
||||
import org.w3c.dom.HTMLElement
|
||||
import space.kscience.dataforge.context.AbstractPlugin
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.context.PluginFactory
|
||||
import space.kscience.dataforge.context.PluginTag
|
||||
import space.kscience.dataforge.meta.DynamicMeta
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.toDynamic
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.asName
|
||||
import space.kscience.visionforge.ElementVisionRenderer
|
||||
import space.kscience.visionforge.Vision
|
||||
import space.kscience.visionforge.VisionClient
|
||||
import tabulator.Tabulator
|
||||
import tabulator.TabulatorFull
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
public class TableVisionJsPlugin : AbstractPlugin(), ElementVisionRenderer {
|
||||
public val visionClient: VisionClient by require(VisionClient)
|
||||
public val tablesBase: TableVisionPlugin by require(TableVisionPlugin)
|
||||
|
||||
override val tag: PluginTag get() = Companion.tag
|
||||
|
||||
override fun attach(context: Context) {
|
||||
super.attach(context)
|
||||
kotlinext.js.require("tabulator-tables/dist/css/tabulator.min.css")
|
||||
kotlinext.js.require("tabulator-tables/src/js/modules/ResizeColumns/ResizeColumns.js")
|
||||
}
|
||||
|
||||
override fun rateVision(vision: Vision): Int = when (vision) {
|
||||
is VisionOfTable -> ElementVisionRenderer.DEFAULT_RATING
|
||||
else -> ElementVisionRenderer.ZERO_RATING
|
||||
}
|
||||
|
||||
override fun render(element: Element, vision: Vision, meta: Meta) {
|
||||
val table: VisionOfTable = (vision as? VisionOfTable)
|
||||
?: error("VisionOfTable expected but ${vision::class} found")
|
||||
|
||||
val tableOptions = jso<Tabulator.Options> {
|
||||
columns = table.headers.map { header ->
|
||||
jso<Tabulator.ColumnDefinition> {
|
||||
field = header.name
|
||||
title = header.properties.title ?: header.name
|
||||
resizable = true
|
||||
}
|
||||
}.toTypedArray()
|
||||
|
||||
columns = Array(table.headers.size + 1){
|
||||
if(it==0){
|
||||
jso {
|
||||
field = "@index"
|
||||
title = "#"
|
||||
resizable = false
|
||||
}
|
||||
} else {
|
||||
val header = table.headers[it-1]
|
||||
jso {
|
||||
field = header.name
|
||||
title = header.properties.title ?: header.name
|
||||
resizable = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
data = table.rows.mapIndexed { index, row->
|
||||
val d = row.meta.toDynamic()
|
||||
d["@index"] = index
|
||||
d
|
||||
}.toTypedArray()
|
||||
|
||||
//layout = "fitColumns"
|
||||
|
||||
pagination = true
|
||||
paginationSize = 10
|
||||
paginationSizeSelector = arrayOf(10, 25, 50, 100)
|
||||
}
|
||||
|
||||
TabulatorFull(element as HTMLElement, tableOptions)
|
||||
}
|
||||
|
||||
override fun content(target: String): Map<Name, Any> = when (target) {
|
||||
ElementVisionRenderer.TYPE -> mapOf("table".asName() to this)
|
||||
else -> super.content(target)
|
||||
}
|
||||
|
||||
public companion object : PluginFactory<TableVisionJsPlugin> {
|
||||
override val tag: PluginTag = PluginTag("vision.table.js", PluginTag.DATAFORGE_GROUP)
|
||||
override val type: KClass<TableVisionJsPlugin> = TableVisionJsPlugin::class
|
||||
override fun invoke(meta: Meta, context: Context): TableVisionJsPlugin = TableVisionJsPlugin()
|
||||
}
|
||||
}
|
2347
visionforge-tables/src/jsMain/kotlin/tabulator/Tabulator.kt
Normal file
2347
visionforge-tables/src/jsMain/kotlin/tabulator/Tabulator.kt
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,45 @@
|
||||
@file:Suppress("NO_EXPLICIT_VISIBILITY_IN_API_MODE_WARNING")
|
||||
|
||||
package tabulator
|
||||
|
||||
import org.w3c.dom.events.UIEvent
|
||||
|
||||
@Suppress("UNUSED_TYPEALIAS_PARAMETER")
|
||||
internal typealias Pick<T, K> = Any
|
||||
|
||||
@Suppress("UNUSED_TYPEALIAS_PARAMETER")
|
||||
internal typealias Record<K, T> = Any
|
||||
|
||||
internal typealias FilterFunction = (field: String, type: String /* "=" | "!=" | "like" | "<" | ">" | "<=" | ">=" | "in" | "regex" | "starts" | "ends" */, value: Any, filterParams: Tabulator.FilterParams) -> Unit
|
||||
|
||||
internal typealias GroupValuesArg = Array<Array<Any>>
|
||||
|
||||
internal typealias CustomMutator = (value: Any, data: Any, type: String /* "data" | "edit" */, mutatorParams: Any, cell: Tabulator.CellComponent) -> Any
|
||||
|
||||
internal typealias CustomAccessor = (value: Any, data: Any, type: String /* "data" | "download" | "clipboard" */, AccessorParams: Any, column: Tabulator.ColumnComponent, row: Tabulator.RowComponent) -> Any
|
||||
|
||||
internal typealias ColumnCalcParams = (values: Any, data: Any) -> Any
|
||||
|
||||
internal typealias ValueStringCallback = (value: Any) -> String
|
||||
|
||||
internal typealias ValueBooleanCallback = (value: Any) -> Boolean
|
||||
|
||||
internal typealias ValueVoidCallback = (value: Any) -> Unit
|
||||
|
||||
internal typealias EmptyCallback = (callback: () -> Unit) -> Unit
|
||||
|
||||
internal typealias CellEventCallback = (e: UIEvent, cell: Tabulator.CellComponent) -> Unit
|
||||
|
||||
internal typealias CellEditEventCallback = (cell: Tabulator.CellComponent) -> Unit
|
||||
|
||||
internal typealias ColumnEventCallback = (e: UIEvent, column: Tabulator.ColumnComponent) -> Unit
|
||||
|
||||
internal typealias RowEventCallback = (e: UIEvent, row: Tabulator.RowComponent) -> Unit
|
||||
|
||||
internal typealias RowChangedCallback = (row: Tabulator.RowComponent) -> Unit
|
||||
|
||||
internal typealias GroupEventCallback = (e: UIEvent, group: Tabulator.GroupComponent) -> Unit
|
||||
|
||||
internal typealias JSONRecord = Record<String, dynamic /* String | Number | Boolean */>
|
||||
|
||||
internal typealias ColumnSorterParamLookupFunction = (column: Tabulator.ColumnComponent, dir: String /* "asc" | "desc" */) -> Any
|
@ -5,6 +5,7 @@ import info.laht.threekt.core.Object3D
|
||||
import info.laht.threekt.geometries.TextBufferGeometry
|
||||
import info.laht.threekt.objects.Mesh
|
||||
import kotlinext.js.jsObject
|
||||
import kotlinext.js.jso
|
||||
import space.kscience.dataforge.context.logger
|
||||
import space.kscience.dataforge.context.warn
|
||||
import space.kscience.visionforge.onPropertyChange
|
||||
@ -18,7 +19,7 @@ public object ThreeLabelFactory : ThreeFactory<SolidLabel> {
|
||||
override val type: KClass<in SolidLabel> get() = SolidLabel::class
|
||||
|
||||
override fun invoke(three: ThreePlugin, obj: SolidLabel): Object3D {
|
||||
val textGeo = TextBufferGeometry(obj.text, jsObject {
|
||||
val textGeo = TextBufferGeometry(obj.text, jso {
|
||||
font = obj.fontFamily
|
||||
size = 20
|
||||
height = 1
|
||||
|
Loading…
Reference in New Issue
Block a user