Compare commits
18 Commits
refactor/c
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
| 2c55875f86 | |||
| c543536d2d | |||
| efb05ae156 | |||
| ba4a8aa7c1 | |||
| d069bd6047 | |||
| 0268ddbca0 | |||
| eacf977a0e | |||
| 3c20583b5e | |||
| 2bb20dea68 | |||
| 41949329ba | |||
| d578efa17b | |||
| b6100bdce1 | |||
| 636938087a | |||
| 9782721cb9 | |||
| 29dacbdd2f | |||
| 6b83137bd0 | |||
| d48133f77f | |||
| ad5ec93598 |
22
.github/workflows/build.yml
vendored
22
.github/workflows/build.yml
vendored
@@ -2,7 +2,7 @@ name: Gradle build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ dev, master ]
|
||||
branches: [ dev, main ]
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
@@ -10,15 +10,25 @@ jobs:
|
||||
runs-on: windows-latest
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-java@v3.5.1
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-java@v5
|
||||
with:
|
||||
java-version: '17'
|
||||
java-version: '21'
|
||||
distribution: 'liberica'
|
||||
cache: 'gradle'
|
||||
- name: Gradle Wrapper Validation
|
||||
uses: gradle/wrapper-validation-action@v1.0.4
|
||||
- name: Gradle Build
|
||||
uses: gradle/gradle-build-action@v2.4.2
|
||||
uses: gradle/gradle-build-action@v3
|
||||
with:
|
||||
arguments: test jvmTest
|
||||
arguments: test jvmTest
|
||||
- name: Publish Test Report
|
||||
uses: mikepenz/action-junit-report@v6
|
||||
if: ${{ !cancelled() }} # always run even if the previous step fails
|
||||
with:
|
||||
report_paths: '**/test-results/**/TEST-*.xml'
|
||||
annotate_only: true
|
||||
detailed_summary: true
|
||||
flaky_summary: true
|
||||
include_empty_in_summary: false
|
||||
skip_success_summary: true
|
||||
2
.github/workflows/pages.yml
vendored
2
.github/workflows/pages.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 17
|
||||
java-version: 21
|
||||
distribution: liberica
|
||||
- uses: gradle/gradle-build-action@v3
|
||||
with:
|
||||
|
||||
19
CHANGELOG.md
19
CHANGELOG.md
@@ -3,18 +3,35 @@
|
||||
## Unreleased
|
||||
|
||||
### Added
|
||||
- Plotly implementation and demo for Kotlin/Wasm
|
||||
- Wasm targets for plotly-kt and visionforge-core/solid
|
||||
- Time (Instant) based TraceValues in Plotly
|
||||
|
||||
### Changed
|
||||
- Order of arguments in Plotly-kt for js plotDiv functions
|
||||
|
||||
### Deprecated
|
||||
|
||||
### Removed
|
||||
|
||||
### Fixed
|
||||
- Fix the problem where property listeners do not react on property child node changa
|
||||
- Plotly-kt js demo
|
||||
|
||||
### Security
|
||||
|
||||
## 0.5.1 - 2026-01-10
|
||||
|
||||
### Changed
|
||||
|
||||
- Kotlin 2.3
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix DSLMarker annotations for builders
|
||||
- Flaky test
|
||||
- Fix the problem where property listeners do not react on property child node changes
|
||||
- Plotly plot title now writes proper field
|
||||
|
||||
## 0.5.0 - 2025-03-21
|
||||
|
||||
### Added
|
||||
|
||||
40
README.md
40
README.md
@@ -5,14 +5,14 @@
|
||||
|
||||
[](https://kotlinlang.slack.com/archives/CEXV2QWNM)
|
||||
|
||||
# DataForge Visualization Platform
|
||||
# VisionForge platform
|
||||
|
||||
## Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Requirements](#requirements)
|
||||
* [Features](#features)
|
||||
* [About DataForge](#about-dataforge)
|
||||
* [About VisionForge](#about-VisionForge)
|
||||
* [Modules contained in this repository](#modules-contained-in-this-repository)
|
||||
* [Visualization for External Systems](#visualization-for-external-systems)
|
||||
* [Demonstrations](#demonstrations)
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
## Introduction
|
||||
|
||||
This repository contains a [DataForge](#about-dataforge)\-based framework
|
||||
This repository contains a [VisionForge](#about-VisionForge) framework
|
||||
used for visualization in various scientific applications.
|
||||
|
||||
The main framework's use case for now is 3D visualization for particle physics experiments.
|
||||
@@ -41,16 +41,16 @@ JVM backend requires JDK 11 or later
|
||||
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
|
||||
- Scales up to hundreds of thousands of elements
|
||||
- The camera moves, rotates, zoom-in and zoom-out
|
||||
- Scene graph as an object tree with property editor
|
||||
- Settings export and import
|
||||
- Multiple platform support
|
||||
|
||||
## About DataForge
|
||||
## About VisionForge
|
||||
|
||||
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`,
|
||||
[DataForge](https://git.sciprog.center/kscience/dataforge-core) is a software framework for automated scientific data processing. VisionForge
|
||||
Platform uses some concepts and modules of DataForge, including: `Meta`, `Configuration`, `Context`,
|
||||
`Provider`, and some others.
|
||||
|
||||
To learn more about DataForge, please consult the following URLs:
|
||||
@@ -66,10 +66,6 @@ To learn more about DataForge, please consult the following URLs:
|
||||
>
|
||||
> **Maturity**: EXPERIMENTAL
|
||||
|
||||
### [demo](demo)
|
||||
>
|
||||
> **Maturity**: EXPERIMENTAL
|
||||
|
||||
### [plotly-kt](plotly-kt)
|
||||
>
|
||||
> **Maturity**: EXPERIMENTAL
|
||||
@@ -131,10 +127,6 @@ To learn more about DataForge, please consult the following URLs:
|
||||
>
|
||||
> **Maturity**: EXPERIMENTAL
|
||||
|
||||
### [demo/playground](demo/playground)
|
||||
>
|
||||
> **Maturity**: EXPERIMENTAL
|
||||
|
||||
### [demo/sat-demo](demo/sat-demo)
|
||||
>
|
||||
> **Maturity**: EXPERIMENTAL
|
||||
@@ -143,10 +135,6 @@ To learn more about DataForge, please consult the following URLs:
|
||||
>
|
||||
> **Maturity**: EXPERIMENTAL
|
||||
|
||||
### [plotly-kt/examples](plotly-kt/examples)
|
||||
>
|
||||
> **Maturity**: EXPERIMENTAL
|
||||
|
||||
### [plotly-kt/plotly-kt-core](plotly-kt/plotly-kt-core)
|
||||
>
|
||||
> **Maturity**: DEVELOPMENT
|
||||
@@ -164,18 +152,6 @@ To learn more about DataForge, please consult the following URLs:
|
||||
>
|
||||
> **Maturity**: EXPERIMENTAL
|
||||
|
||||
### [plotly-kt/examples/compose-demo](plotly-kt/examples/compose-demo)
|
||||
>
|
||||
> **Maturity**: EXPERIMENTAL
|
||||
|
||||
### [plotly-kt/examples/js-demo](plotly-kt/examples/js-demo)
|
||||
>
|
||||
> **Maturity**: EXPERIMENTAL
|
||||
|
||||
### [plotly-kt/examples/native-demo](plotly-kt/examples/native-demo)
|
||||
>
|
||||
> **Maturity**: EXPERIMENTAL
|
||||
|
||||
|
||||
**Class diagram:**
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
@file:OptIn(ExperimentalAbiValidation::class)
|
||||
|
||||
import org.jetbrains.kotlin.gradle.dsl.abi.ExperimentalAbiValidation
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
import space.kscience.gradle.useApache2Licence
|
||||
import space.kscience.gradle.useSPCTeam
|
||||
@@ -7,11 +10,11 @@ plugins {
|
||||
alias(spclibs.plugins.kotlinx.kover)
|
||||
}
|
||||
|
||||
val dataforgeVersion by extra("0.10.1")
|
||||
val dataforgeVersion by extra("0.10.2")
|
||||
|
||||
allprojects {
|
||||
group = "space.kscience"
|
||||
version = "0.6.0-dev-1"
|
||||
version = "0.5.2-dev-1"
|
||||
}
|
||||
|
||||
subprojects {
|
||||
@@ -39,17 +42,23 @@ subprojects {
|
||||
}
|
||||
|
||||
|
||||
ksciencePublish {
|
||||
kscienceProject {
|
||||
pom("https://github.com/SciProgCentre/visionforge") {
|
||||
useApache2Licence()
|
||||
useSPCTeam()
|
||||
}
|
||||
repository("spc", "https://maven.sciprog.center/kscience")
|
||||
central()
|
||||
publishTo("spc", "https://maven.sciprog.center/kscience")
|
||||
publishToCentral()
|
||||
|
||||
abiValidation {
|
||||
filters{
|
||||
exclude{
|
||||
byNames.add("info.laht.threekt.**")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
readme.readmeTemplate = file("docs/templates/README-TEMPLATE.md")
|
||||
}
|
||||
|
||||
apiValidation {
|
||||
ignoredPackages.add("info.laht.threekt")
|
||||
}
|
||||
|
||||
readme.readmeTemplate = file("docs/templates/README-TEMPLATE.md")
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
## Artifact:
|
||||
|
||||
The Maven coordinates of this project are `space.kscience:cern-root-loader:0.5.0`.
|
||||
The Maven coordinates of this project are `space.kscience:cern-root-loader:0.5.1`.
|
||||
|
||||
**Gradle Kotlin DSL:**
|
||||
```kotlin
|
||||
@@ -16,6 +16,6 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("space.kscience:cern-root-loader:0.5.0")
|
||||
implementation("space.kscience:cern-root-loader:0.5.1")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -81,10 +81,12 @@ fun GravityDemo(context: Context) {
|
||||
y = h
|
||||
|
||||
|
||||
box(200, 5, 200, name = "floor") {
|
||||
y = -2.5
|
||||
}
|
||||
}
|
||||
|
||||
box(200, 5, 200, name = "floor") {
|
||||
y = -2.5
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,11 +12,7 @@ kscience {
|
||||
"muon-monitor.js",
|
||||
development = false,
|
||||
jvmConfig = {
|
||||
binaries {
|
||||
executable {
|
||||
mainClass.set("ru.mipt.npm.muon.monitor.MMServerKt")
|
||||
}
|
||||
}
|
||||
application("ru.mipt.npm.muon.monitor.MMServerKt")
|
||||
},
|
||||
browserConfig = {
|
||||
commonWebpackConfig {
|
||||
|
||||
@@ -12,7 +12,7 @@ repositories {
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvmToolchain(17)
|
||||
jvmToolchain(21)
|
||||
js {
|
||||
useEsModules()
|
||||
browser {
|
||||
|
||||
@@ -49,7 +49,7 @@ suspend fun main() = serve(
|
||||
//axes(200)
|
||||
ambientLight {
|
||||
color(Colors.white)
|
||||
intensity = 3.0
|
||||
intensity = 1.5
|
||||
}
|
||||
val platform = solidGroup("platform") {
|
||||
cylinder(50, 5, name = "base")
|
||||
@@ -88,7 +88,7 @@ suspend fun main() = serve(
|
||||
val incRot = Quaternion.fromRotation(30.degrees, Float64Space3D.zAxis)
|
||||
|
||||
|
||||
context.launch {
|
||||
this@vision.context.launch {
|
||||
var time: Long = 0L
|
||||
while (isActive) {
|
||||
with(QuaternionAlgebra) {
|
||||
|
||||
@@ -13,14 +13,10 @@ kscience {
|
||||
// useSerialization {
|
||||
// json()
|
||||
// }
|
||||
jvm{
|
||||
binaries {
|
||||
executable {
|
||||
mainClass.set("ru.mipt.npm.sat.SatServerKt")
|
||||
}
|
||||
}
|
||||
jvm {
|
||||
application("ru.mipt.npm.sat.SatServerKt")
|
||||
}
|
||||
jvmMain{
|
||||
jvmMain {
|
||||
implementation("io.ktor:ktor-server-cio")
|
||||
implementation(projects.visionforgeThreejs.visionforgeThreejsServer)
|
||||
implementation(spclibs.logback.classic)
|
||||
|
||||
@@ -8,4 +8,4 @@ org.gradle.workers.max=4
|
||||
org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled
|
||||
kotlin.native.enableKlibsCrossCompilation=true
|
||||
|
||||
toolsVersion=0.19.2-kotlin-2.2.20
|
||||
toolsVersion=0.20.2-kotlin-2.3.0
|
||||
|
||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
@@ -15,10 +15,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
### Removed
|
||||
|
||||
### Fixed
|
||||
- Fix legend orientation constants
|
||||
|
||||
### Security
|
||||
|
||||
## 0.5.1 - 2026-01-10
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix legend orientation constants
|
||||
|
||||
## 0.5.0 (package and versioning change!)
|
||||
|
||||
### Changed
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
plugins{
|
||||
id("org.jetbrains.changelog")
|
||||
id("space.kscience.gradle.project")
|
||||
}
|
||||
|
||||
kscienceProject{
|
||||
readme {
|
||||
readmeTemplate = file("docs/templates/README-TEMPLATE.md")
|
||||
}
|
||||
}
|
||||
|
||||
readme {
|
||||
readmeTemplate = file("docs/templates/README-TEMPLATE.md")
|
||||
}
|
||||
@@ -15,7 +15,7 @@ dependencies {
|
||||
}
|
||||
|
||||
kotlin{
|
||||
jvmToolchain(17)
|
||||
jvmToolchain(21)
|
||||
}
|
||||
|
||||
// A workaround for https://youtrack.jetbrains.com/issue/KT-44101
|
||||
|
||||
@@ -12,7 +12,7 @@ repositories {
|
||||
|
||||
kotlin {
|
||||
jvm()
|
||||
jvmToolchain(17)
|
||||
jvmToolchain(21)
|
||||
sourceSets {
|
||||
jvmMain {
|
||||
dependencies {
|
||||
|
||||
@@ -11,11 +11,14 @@ import kotlinx.html.style
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.w3c.dom.HTMLElement
|
||||
import org.w3c.dom.events.Event
|
||||
import space.kscience.dataforge.context.Global
|
||||
import space.kscience.dataforge.meta.MetaSerializer
|
||||
import space.kscience.plotly.*
|
||||
import space.kscience.plotly.events.PlotlyEventListenerType
|
||||
import space.kscience.plotly.models.ScatterMode
|
||||
import space.kscience.plotly.models.TraceType
|
||||
import space.kscience.plotly.models.histogram
|
||||
import space.kscience.plotly.models.scatter
|
||||
import kotlin.random.Random
|
||||
|
||||
private fun onDomLoaded(block: (Event) -> Unit) {
|
||||
@@ -35,7 +38,7 @@ fun main(): Unit = withCanvas {
|
||||
div {
|
||||
style = "height:50%; width=100%;"
|
||||
h1 { +"Histogram demo" }
|
||||
plotDiv {
|
||||
plotDiv(Global) {
|
||||
val rnd = Random(222)
|
||||
histogram {
|
||||
name = "Random data"
|
||||
@@ -79,7 +82,7 @@ fun main(): Unit = withCanvas {
|
||||
div {
|
||||
style = "height:50%; width=100%;"
|
||||
h1 { +"Dynamic trace demo" }
|
||||
plotDiv {
|
||||
plotDiv(Global) {
|
||||
scatter {
|
||||
x(1, 2, 3, 4)
|
||||
y(10, 15, 13, 17)
|
||||
@@ -15,14 +15,14 @@ import space.kscience.plotly.models.geo.openStreetMap
|
||||
import space.kscience.plotly.plot
|
||||
import space.kscience.visionforge.plotly.serveSinglePage
|
||||
import space.kscience.visionforge.server.openInBrowser
|
||||
import java.net.URL
|
||||
import java.net.URI
|
||||
import kotlin.random.Random
|
||||
|
||||
suspend fun main() {
|
||||
|
||||
//downloading GeoJson
|
||||
val geoJsonString =
|
||||
URL("https://raw.githubusercontent.com/isellsoap/deutschlandGeoJSON/main/4_kreise/4_niedrig.geo.json").readText()
|
||||
URI("https://raw.githubusercontent.com/isellsoap/deutschlandGeoJSON/main/4_kreise/4_niedrig.geo.json").toURL().readText()
|
||||
|
||||
|
||||
// Filtering GeoJson features and creating new feature set
|
||||
@@ -50,7 +50,7 @@ suspend fun main() {
|
||||
locations.numbers = features.map { it.id!!.int }
|
||||
// Set random values to locations
|
||||
z.numbers = features.map { Random.nextDouble(1.0, 10.0) }
|
||||
context.launch {
|
||||
this@serveSinglePage.context.launch {
|
||||
while (isActive) {
|
||||
delay(300)
|
||||
z.numbers = features.map { Random.nextDouble(1.0, 10.0) }
|
||||
|
||||
@@ -10,7 +10,7 @@ import space.kscience.plotly.models.geo.json.GeoJsonFeatureCollection
|
||||
import space.kscience.plotly.models.geo.json.combine
|
||||
import space.kscience.plotly.models.geo.openStreetMap
|
||||
import space.kscience.plotly.openInBrowser
|
||||
import java.net.URL
|
||||
import java.net.URI
|
||||
import kotlin.random.Random
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ fun main() {
|
||||
|
||||
//downloading GeoJson
|
||||
val geoJsonString =
|
||||
URL("https://raw.githubusercontent.com/isellsoap/deutschlandGeoJSON/main/4_kreise/4_niedrig.geo.json").readText()
|
||||
URI("https://raw.githubusercontent.com/isellsoap/deutschlandGeoJSON/main/4_kreise/4_niedrig.geo.json").toURL().readText()
|
||||
|
||||
|
||||
// Filtering GeoJson features and creating new feature set
|
||||
|
||||
29
plotly-kt/examples/wasm-demo/build.gradle.kts
Normal file
29
plotly-kt/examples/wasm-demo/build.gradle.kts
Normal file
@@ -0,0 +1,29 @@
|
||||
@file:OptIn(ExperimentalWasmDsl::class)
|
||||
|
||||
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
|
||||
|
||||
plugins {
|
||||
kotlin("multiplatform")
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven("https://repo.kotlin.link")
|
||||
}
|
||||
|
||||
kotlin {
|
||||
|
||||
wasmJs {
|
||||
browser()
|
||||
binaries.executable()
|
||||
}
|
||||
|
||||
sourceSets{
|
||||
wasmJsMain{
|
||||
dependencies{
|
||||
implementation(projects.plotlyKt.plotlyKtCore)
|
||||
implementation(spclibs.kotlinx.coroutines.core)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
146
plotly-kt/examples/wasm-demo/src/wasmJsMain/kotlin/main.kt
Normal file
146
plotly-kt/examples/wasm-demo/src/wasmJsMain/kotlin/main.kt
Normal file
@@ -0,0 +1,146 @@
|
||||
package space.kscience.plotly.wasmjsdemo
|
||||
|
||||
|
||||
import kotlinx.browser.document
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.html.TagConsumer
|
||||
import kotlinx.html.dom.append
|
||||
import kotlinx.html.h1
|
||||
import kotlinx.html.js.div
|
||||
import kotlinx.html.style
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.w3c.dom.Element
|
||||
import org.w3c.dom.HTMLElement
|
||||
import org.w3c.dom.events.Event
|
||||
import space.kscience.dataforge.context.Global
|
||||
import space.kscience.dataforge.meta.MetaSerializer
|
||||
import space.kscience.plotly.*
|
||||
import space.kscience.plotly.models.ScatterMode
|
||||
import space.kscience.plotly.models.TraceType
|
||||
import space.kscience.plotly.models.histogram
|
||||
import space.kscience.plotly.models.scatter
|
||||
import kotlin.random.Random
|
||||
|
||||
private fun onDomLoaded(block: (Event) -> Unit) {
|
||||
document.addEventListener("DOMContentLoaded", block)
|
||||
}
|
||||
|
||||
private fun withCanvas(block: TagConsumer<Element>.() -> Unit) {
|
||||
val element = document.getElementById("canvas") as? HTMLElement
|
||||
?: error("Element with id 'canvas' not found on page")
|
||||
println("element loaded")
|
||||
element.append { block() }
|
||||
}
|
||||
|
||||
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
fun main(): Unit = withCanvas {
|
||||
div {
|
||||
style = "height:50%; width=100%;"
|
||||
h1 { +"Histogram demo" }
|
||||
plotDiv(Global) {
|
||||
val rnd = Random(222)
|
||||
histogram {
|
||||
name = "Random data"
|
||||
GlobalScope.launch {
|
||||
while (isActive) {
|
||||
x.numbers = List(500) { rnd.nextDouble() }
|
||||
delay(300)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layout {
|
||||
bargap = 0.1
|
||||
title {
|
||||
text = "Basic Histogram"
|
||||
font {
|
||||
size = 20
|
||||
color("black")
|
||||
}
|
||||
}
|
||||
xaxis {
|
||||
title {
|
||||
text = "Value"
|
||||
font {
|
||||
size = 16
|
||||
}
|
||||
}
|
||||
}
|
||||
yaxis {
|
||||
title {
|
||||
text = "Count"
|
||||
font {
|
||||
size = 16
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
div {
|
||||
style = "height:50%; width=100%;"
|
||||
h1 { +"Dynamic trace demo" }
|
||||
plotDiv(Global) {
|
||||
scatter {
|
||||
x(1, 2, 3, 4)
|
||||
y(10, 15, 13, 17)
|
||||
mode = ScatterMode.markers
|
||||
type = TraceType.scatter
|
||||
}
|
||||
scatter {
|
||||
x(2, 3, 4, 5)
|
||||
y(10, 15, 13, 17)
|
||||
mode = ScatterMode.lines
|
||||
type = TraceType.scatter
|
||||
|
||||
GlobalScope.launch {
|
||||
while (isActive) {
|
||||
delay(500)
|
||||
marker {
|
||||
if (Random.nextBoolean()) {
|
||||
color("magenta")
|
||||
} else {
|
||||
color("blue")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
scatter {
|
||||
x(1, 2, 3, 4)
|
||||
y(12, 5, 2, 12)
|
||||
mode = ScatterMode.`lines+markers`
|
||||
type = TraceType.scatter
|
||||
marker {
|
||||
color("red")
|
||||
}
|
||||
}
|
||||
layout {
|
||||
title = "Line and Scatter Plot"
|
||||
}
|
||||
}
|
||||
}
|
||||
div {
|
||||
style = "height:50%; width=100%;"
|
||||
h1 { +"Deserialization" }
|
||||
val plot = Plotly.plot {
|
||||
scatter {
|
||||
x(1, 2, 3, 4)
|
||||
y(10, 15, 13, 17)
|
||||
mode = ScatterMode.markers
|
||||
type = TraceType.scatter
|
||||
}
|
||||
}
|
||||
val serialized = plot.toJsonString()
|
||||
println(serialized)
|
||||
val deserialized = Plot(Json.decodeFromString(MetaSerializer, serialized))
|
||||
plotDiv(plot = deserialized)
|
||||
// plotDiv(plot = deserialized).on(PlotlyEventListenerType.CLICK){
|
||||
// println(it.toString())
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
|
||||
<title>Kotlin/Wasm Example</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script src="wasm-demo.js"></script>
|
||||
|
||||
<div id="canvas"></div>
|
||||
|
||||
</body>
|
||||
|
||||
<script type="application/javascript">
|
||||
const unhandledError = (event, error) => {
|
||||
if (error instanceof WebAssembly.CompileError) {
|
||||
document.getElementById("warning").style.display = "initial";
|
||||
|
||||
// Hide a Scary Webpack Overlay which is less informative in this case.
|
||||
const webpackOverlay = document.getElementById("webpack-dev-server-client-overlay");
|
||||
if (webpackOverlay != null) webpackOverlay.style.display = "none";
|
||||
}
|
||||
}
|
||||
addEventListener("error", (event) => unhandledError(event, event.error));
|
||||
addEventListener("unhandledrejection", (event) => unhandledError(event, event.reason));
|
||||
</script>
|
||||
|
||||
</html>
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
## Artifact:
|
||||
|
||||
The Maven coordinates of this project are `space.kscience:plotly-kt-core:0.5.0`.
|
||||
The Maven coordinates of this project are `space.kscience:plotly-kt-core:0.5.1`.
|
||||
|
||||
**Gradle Kotlin DSL:**
|
||||
```kotlin
|
||||
@@ -16,6 +16,6 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("space.kscience:plotly-kt-core:0.5.0")
|
||||
implementation("space.kscience:plotly-kt-core:0.5.1")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -11,11 +11,9 @@ val plotlyVersion by extra("2.35.3")
|
||||
//}
|
||||
|
||||
kscience {
|
||||
// jvm()
|
||||
// js()
|
||||
fullStack(bundleName = "js/plotly-kt.js")
|
||||
native()
|
||||
// wasm()
|
||||
wasmJs()
|
||||
useSerialization()
|
||||
|
||||
commonMain {
|
||||
@@ -29,12 +27,18 @@ kscience {
|
||||
nativeMain {
|
||||
implementation("com.squareup.okio:okio:3.3.0")
|
||||
}
|
||||
|
||||
wasmJsMain {
|
||||
api(npm("plotly.js", plotlyVersion))
|
||||
api("org.jetbrains.kotlinx:kotlinx-browser:0.5.0")
|
||||
}
|
||||
}
|
||||
|
||||
//tasks.processJupyterApiResources {
|
||||
// libraryProducers = listOf("space.kscience.plotly.PlotlyIntegration")
|
||||
//}
|
||||
|
||||
kotlinJupyter {
|
||||
integrations {
|
||||
producer("space.kscience.plotly.PlotlyIntegration")
|
||||
}
|
||||
}
|
||||
|
||||
kotlin {
|
||||
compilerOptions {
|
||||
|
||||
@@ -8,7 +8,6 @@ import kotlinx.serialization.json.buildJsonObject
|
||||
import space.kscience.dataforge.meta.*
|
||||
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||
import space.kscience.dataforge.meta.descriptors.node
|
||||
import space.kscience.dataforge.misc.DFBuilder
|
||||
import space.kscience.dataforge.names.NameToken
|
||||
import space.kscience.plotly.models.Layout
|
||||
import space.kscience.plotly.models.Trace
|
||||
@@ -18,7 +17,6 @@ import space.kscience.visionforge.*
|
||||
* The main plot class.
|
||||
*
|
||||
*/
|
||||
@DFBuilder
|
||||
@Serializable
|
||||
public class Plot : AbstractVision(), MutableVisionGroup<Trace> {
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ public class Axis : Scheme() {
|
||||
public var title: String?
|
||||
get() = meta["title.text"].string ?: meta["title"].string
|
||||
set(value) {
|
||||
meta["title"] = value?.asValue()
|
||||
meta["title.text"] = value?.asValue()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -720,13 +720,11 @@ public fun <T : Scheme> Trace.scheme(
|
||||
|
||||
/**
|
||||
* A base class for Plotly traces
|
||||
*
|
||||
* @param uid a unique identifier for this trace
|
||||
*/
|
||||
@Serializable
|
||||
public open class Trace : AbstractVision(), MutableMetaProvider, MetaRepr {
|
||||
|
||||
override fun get(name: Name): MutableMeta? = properties.get(name)
|
||||
override fun get(name: Name): MutableMeta? = properties[name]
|
||||
|
||||
override fun set(name: Name, node: Meta?) {
|
||||
properties[name] = node
|
||||
|
||||
@@ -2,6 +2,7 @@ package space.kscience.plotly.models
|
||||
|
||||
import space.kscience.dataforge.meta.*
|
||||
import space.kscience.dataforge.names.Name
|
||||
import kotlin.time.Instant
|
||||
|
||||
/**
|
||||
* Type-safe accessor class for values in the trace
|
||||
@@ -27,6 +28,12 @@ public class TraceValues internal constructor(public val owner: MutableMetaProvi
|
||||
this.value = value.map { it.asValue() }.asValue()
|
||||
}
|
||||
|
||||
public var times: Iterable<Instant?>
|
||||
get() = value?.list?.map { Instant.parseOrNull(it.string) } ?: emptyList()
|
||||
set(value) {
|
||||
this.value = value.map { it.toString().asValue() }.asValue()
|
||||
}
|
||||
|
||||
/**
|
||||
* Smart fill for trace values. The following types are accepted: [DoubleArray], [IntArray], [Array] of primitive or string,
|
||||
* [Iterable] of primitive or string.
|
||||
@@ -50,6 +57,10 @@ public class TraceValues internal constructor(public val owner: MutableMetaProvi
|
||||
this.strings = strings.asList()
|
||||
}
|
||||
|
||||
public operator fun invoke(vararg times: Instant) {
|
||||
this.times = times.asList()
|
||||
}
|
||||
|
||||
public operator fun invoke(lists: List<List<Number>>) {
|
||||
this.value = lists.map { row -> row.map { it.asValue() }.asValue() }.asValue()
|
||||
}
|
||||
|
||||
@@ -61,9 +61,10 @@ public fun Element.plot(
|
||||
trace.eventFlow.filterIsInstance<VisionPropertyChangedEvent>().onEach { event ->
|
||||
val traceData = trace.toDynamic()
|
||||
|
||||
//need to wrap coordinates into an additional array because plotly API for some reason expects 2D arrays
|
||||
Plotly.coordinateNames.forEach { coordinate ->
|
||||
val data = traceData[coordinate]
|
||||
if (traceData[coordinate] != null) {
|
||||
if (data != null) {
|
||||
traceData[coordinate] = arrayOf(data)
|
||||
}
|
||||
}
|
||||
@@ -111,12 +112,24 @@ public class PlotlyElement(public val div: HTMLElement)
|
||||
* Create a div element and render the plot in it
|
||||
*/
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
@Deprecated("Change arguments positions", ReplaceWith("plotDiv(plot, plotlyConfig, scope)"))
|
||||
public fun TagConsumer<HTMLElement>.plotDiv(
|
||||
plotlyConfig: PlotlyConfig,
|
||||
plot: Plot,
|
||||
scope: CoroutineScope = plot.manager?.context ?: GlobalScope,
|
||||
): PlotlyElement = PlotlyElement(div("plotly-kt-plot").apply { plot(plotlyConfig, plot) })
|
||||
|
||||
|
||||
/**
|
||||
* Create a div element and render the plot in it
|
||||
*/
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
public fun TagConsumer<HTMLElement>.plotDiv(
|
||||
plot: Plot,
|
||||
plotlyConfig: PlotlyConfig = PlotlyConfig(),
|
||||
scope: CoroutineScope = plot.manager?.context ?: GlobalScope,
|
||||
): PlotlyElement = PlotlyElement(div("plotly-kt-plot").apply { plot(plotlyConfig, plot) })
|
||||
|
||||
/**
|
||||
* Render plot in the HTML element using direct plotly API.
|
||||
*/
|
||||
|
||||
@@ -7,7 +7,6 @@ import okio.Path.Companion.toPath
|
||||
/**
|
||||
* Create a standalone html with the plot
|
||||
* @param path the reference to html file. If null, create a temporary file
|
||||
* @param show if true, start the browser after file is created
|
||||
* @param config represents plotly frame configuration
|
||||
*/
|
||||
@UnstablePlotlyAPI
|
||||
|
||||
57
plotly-kt/plotly-kt-core/src/wasmJsMain/kotlin/PlotlyWasm.kt
Normal file
57
plotly-kt/plotly-kt-core/src/wasmJsMain/kotlin/PlotlyWasm.kt
Normal file
@@ -0,0 +1,57 @@
|
||||
@file:OptIn(ExperimentalWasmJsInterop::class)
|
||||
|
||||
package space.kscience.plotly
|
||||
|
||||
import org.w3c.dom.Element
|
||||
import org.w3c.dom.events.MouseEvent
|
||||
import kotlin.js.Promise
|
||||
|
||||
public external interface ToImgOpts {
|
||||
public var format: JsString /* 'jpeg' | 'png' | 'webp' | 'svg' */
|
||||
public var width: JsNumber
|
||||
public var height: JsNumber
|
||||
}
|
||||
|
||||
public external interface DownloadImgOpts {
|
||||
public var format: JsString /* 'jpeg' | 'png' | 'webp' | 'svg' */
|
||||
public var width: JsNumber
|
||||
public var height: JsNumber
|
||||
public var filename: JsString
|
||||
}
|
||||
|
||||
|
||||
@JsName("Plotly")
|
||||
@JsModule("plotly.js/dist/plotly.js")
|
||||
public external object PlotlyWasm {
|
||||
public fun newPlot(
|
||||
graphDiv: Element,
|
||||
data: JsArray<JsAny> = definedExternally,
|
||||
layout: JsAny = definedExternally,
|
||||
config: JsAny = definedExternally
|
||||
)
|
||||
|
||||
public fun react(
|
||||
graphDiv: Element,
|
||||
data: JsArray<JsAny> = definedExternally,
|
||||
layout: JsAny = definedExternally,
|
||||
config: JsAny = definedExternally
|
||||
)
|
||||
|
||||
public fun update(
|
||||
graphDiv: Element,
|
||||
data: JsAny = definedExternally,
|
||||
layout: JsAny = definedExternally
|
||||
)
|
||||
|
||||
public fun restyle(graphDiv: Element, update: JsAny, traceIndices: JsArray<JsNumber>? = definedExternally)
|
||||
public fun relayout(graphDiv: Element, update: JsAny)
|
||||
|
||||
public fun toImage(root: Element, opts: ToImgOpts): Promise<JsString>
|
||||
public fun downloadImage(root: Element, opts: DownloadImgOpts): Promise<JsString>
|
||||
}
|
||||
|
||||
|
||||
public external interface PlotMouseEvent {
|
||||
public val points: JsArray<JsAny>
|
||||
public val event: MouseEvent
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
@file:OptIn(ExperimentalWasmJsInterop::class)
|
||||
|
||||
package space.kscience.plotly
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.flow.filterIsInstance
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.html.TagConsumer
|
||||
import kotlinx.html.div
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonArray
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import org.w3c.dom.Element
|
||||
import org.w3c.dom.MutationObserver
|
||||
import org.w3c.dom.MutationObserverInit
|
||||
import org.w3c.dom.MutationRecord
|
||||
import space.kscience.dataforge.meta.MetaRepr
|
||||
import space.kscience.dataforge.meta.MetaSerializer
|
||||
import space.kscience.dataforge.meta.toJson
|
||||
import space.kscience.plotly.Plotly.coordinateNames
|
||||
import space.kscience.plotly.models.Trace
|
||||
import space.kscience.visionforge.VisionGroupCompositionChangedEvent
|
||||
import space.kscience.visionforge.VisionPropertyChangedEvent
|
||||
|
||||
@JsFun("s => JSON.parse(s)")
|
||||
private external fun json(s: String): JsAny
|
||||
|
||||
private fun JsonElement.toWasmJs(): JsAny {
|
||||
val string = toString()
|
||||
return json(string)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
private fun MetaRepr.toWasmJs(): JsAny = Json.encodeToJsonElement(MetaSerializer, toMeta()).toWasmJs()
|
||||
|
||||
private fun List<MetaRepr>.toWasmJs(): JsArray<JsAny> = map { it.toWasmJs() }.toJsArray()
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
private fun myMutationObserverInit(
|
||||
childList: Boolean?,
|
||||
attributes: Boolean?,
|
||||
): MutationObserverInit = js("({ childList: childList, attributes: attributes})")
|
||||
|
||||
|
||||
/**
|
||||
* Attach a plot to this element or update the existing plot
|
||||
*/
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
public fun Element.plot(
|
||||
plotlyConfig: PlotlyConfig,
|
||||
plot: Plot,
|
||||
scope: CoroutineScope = plot.manager?.context ?: GlobalScope
|
||||
) {
|
||||
//send initial data
|
||||
PlotlyWasm.react(
|
||||
graphDiv = this,
|
||||
data = plot.data.toWasmJs(),
|
||||
layout = plot.layout.toWasmJs(),
|
||||
config = plotlyConfig.toWasmJs()
|
||||
)
|
||||
|
||||
//start updates
|
||||
val listenJob = scope.launch {
|
||||
plot.data.forEachIndexed { index, trace: Trace ->
|
||||
trace.eventFlow.filterIsInstance<VisionPropertyChangedEvent>().onEach { event ->
|
||||
|
||||
val traceMeta = trace.toMeta()
|
||||
|
||||
//wrap coordinates into an additional array because plotly API for some reason expects 2D arrays
|
||||
val traceJson = JsonObject(
|
||||
traceMeta.items.map { (token, item) ->
|
||||
val key = token.toStringUnescaped()
|
||||
val valueUnwrapped = item.toJson()
|
||||
val value = if (key in coordinateNames) JsonArray(listOf(valueUnwrapped)) else valueUnwrapped
|
||||
key to value
|
||||
}.toMap()
|
||||
)
|
||||
|
||||
PlotlyWasm.restyle(this@plot, traceJson.toWasmJs(), listOf(index.toJsNumber()).toJsArray())
|
||||
}.launchIn(this)
|
||||
}
|
||||
|
||||
plot.eventFlow.onEach { event ->
|
||||
when (event) {
|
||||
is VisionGroupCompositionChangedEvent -> PlotlyWasm.react(this@plot, plot.data.toWasmJs())
|
||||
is VisionPropertyChangedEvent -> PlotlyWasm.relayout(this@plot, plot.layout.toWasmJs())
|
||||
else -> {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
}.launchIn(this)
|
||||
}
|
||||
|
||||
//observe node removal to avoid memory leak
|
||||
MutationObserver { records: JsArray<MutationRecord>, _ ->
|
||||
if (records.toList().firstOrNull()?.removedNodes?.length != 0) {
|
||||
listenJob.cancel()
|
||||
}
|
||||
}.observe(this, myMutationObserverInit(childList = true, attributes = false))
|
||||
}
|
||||
|
||||
@Deprecated("Change arguments positions", ReplaceWith("plot(plotlyConfig, plot)"))
|
||||
public fun Element.plot(plot: Plot, plotlyConfig: PlotlyConfig = PlotlyConfig()): Unit = plot(plotlyConfig, plot)
|
||||
|
||||
/**
|
||||
* Create a plot in this element
|
||||
*/
|
||||
public inline fun Element.plot(
|
||||
scope: CoroutineScope,
|
||||
plotlyConfig: PlotlyConfig = PlotlyConfig(),
|
||||
plotBuilder: Plot.() -> Unit
|
||||
) {
|
||||
plot(plotlyConfig, Plot().apply(plotBuilder), scope)
|
||||
}
|
||||
|
||||
public class PlotlyElement(public val div: Element)
|
||||
|
||||
/**
|
||||
* Create a div element and render the plot in it
|
||||
*/
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
public fun TagConsumer<Element>.plotDiv(
|
||||
plot: Plot,
|
||||
plotlyConfig: PlotlyConfig = PlotlyConfig(),
|
||||
scope: CoroutineScope = plot.manager?.context ?: GlobalScope,
|
||||
): PlotlyElement = PlotlyElement(div("plotly-kt-plot").apply { plot(plotlyConfig, plot) })
|
||||
|
||||
/**
|
||||
* Render plot in the HTML element using direct plotly API.
|
||||
*/
|
||||
public inline fun TagConsumer<Element>.plotDiv(
|
||||
scope: CoroutineScope,
|
||||
plotlyConfig: PlotlyConfig = PlotlyConfig(),
|
||||
plotBuilder: Plot.() -> Unit,
|
||||
): PlotlyElement = PlotlyElement(div("plotly-kt-plot").apply { plot(scope, plotlyConfig, plotBuilder) })
|
||||
|
||||
// TODO implement events
|
||||
//@OptIn(ExperimentalSerializationApi::class)
|
||||
//public fun PlotlyElement.on(eventType: PlotlyEventListenerType, block: MouseEvent.(PlotlyEvent) -> Unit) {
|
||||
// div.addEventListener(eventType.eventType) { event: Event ->
|
||||
// val eventData = PlotlyEvent(event.points.map {
|
||||
// PlotlyEventPoint(
|
||||
// curveNumber = it.curveNumber as Int,
|
||||
// pointNumber = it.pointNumber as? Int,
|
||||
// x = Value.of(it.x),
|
||||
// y = Value.of(it.y),
|
||||
// data = Json.decodeFromDynamic(it.data)
|
||||
// )
|
||||
// })
|
||||
// event.event.block(eventData)
|
||||
// }
|
||||
//}
|
||||
@@ -2,3 +2,20 @@
|
||||
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
## Artifact:
|
||||
|
||||
The Maven coordinates of this project are `space.kscience:plotly-kt-server:0.5.1`.
|
||||
|
||||
**Gradle Kotlin DSL:**
|
||||
```kotlin
|
||||
repositories {
|
||||
maven("https://repo.kotlin.link")
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("space.kscience:plotly-kt-server:0.5.1")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -68,5 +68,6 @@ include(
|
||||
// ":plotly:examples:fx-demo",
|
||||
":plotly-kt:examples:compose-demo",
|
||||
":plotly-kt:examples:js-demo",
|
||||
":plotly-kt:examples:native-demo"
|
||||
":plotly-kt:examples:native-demo",
|
||||
":plotly-kt:examples:wasm-demo"
|
||||
)
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
## Artifact:
|
||||
|
||||
The Maven coordinates of this project are `space.kscience:visionforge-compose-html:0.5.0`.
|
||||
The Maven coordinates of this project are `space.kscience:visionforge-compose-html:0.5.1`.
|
||||
|
||||
**Gradle Kotlin DSL:**
|
||||
```kotlin
|
||||
@@ -16,6 +16,6 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("space.kscience:visionforge-compose-html:0.5.0")
|
||||
implementation("space.kscience:visionforge-compose-html:0.5.1")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
## Artifact:
|
||||
|
||||
The Maven coordinates of this project are `space.kscience:visionforge-compose-multiplatform:0.5.0`.
|
||||
The Maven coordinates of this project are `space.kscience:visionforge-compose-multiplatform:0.5.1`.
|
||||
|
||||
**Gradle Kotlin DSL:**
|
||||
```kotlin
|
||||
@@ -16,6 +16,6 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("space.kscience:visionforge-compose-multiplatform:0.5.0")
|
||||
implementation("space.kscience:visionforge-compose-multiplatform:0.5.1")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
## Artifact:
|
||||
|
||||
The Maven coordinates of this project are `space.kscience:visionforge-core:0.5.0`.
|
||||
The Maven coordinates of this project are `space.kscience:visionforge-core:0.5.1`.
|
||||
|
||||
**Gradle Kotlin DSL:**
|
||||
```kotlin
|
||||
@@ -16,6 +16,6 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("space.kscience:visionforge-core:0.5.0")
|
||||
implementation("space.kscience:visionforge-core:0.5.1")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -8,7 +8,7 @@ kscience {
|
||||
jvm()
|
||||
js()
|
||||
native()
|
||||
// wasm()
|
||||
wasmJs()
|
||||
useCoroutines()
|
||||
commonMain {
|
||||
api("space.kscience:dataforge-context:$dataforgeVersion")
|
||||
|
||||
@@ -16,14 +16,12 @@ private tailrec fun styleIsDefined(vision: Vision, reference: StyleReference): B
|
||||
else -> styleIsDefined(vision.parent!!, reference)
|
||||
}
|
||||
|
||||
@VisionBuilder
|
||||
public fun MutableVision.useStyle(reference: StyleReference) {
|
||||
//check that style is defined in a parent
|
||||
//check(styleIsDefined(this, reference)) { "Style reference does not belong to a Vision parent" }
|
||||
useStyle(reference.name)
|
||||
}
|
||||
|
||||
@VisionBuilder
|
||||
public fun MutableVision.style(
|
||||
styleKey: String? = null,
|
||||
builder: MutableMeta.() -> Unit,
|
||||
@@ -33,7 +31,6 @@ public fun MutableVision.style(
|
||||
StyleReference(this, styleName)
|
||||
}
|
||||
|
||||
@VisionBuilder
|
||||
public fun <T : Scheme> MutableVision.style(
|
||||
spec: SchemeSpec<T>,
|
||||
styleKey: String? = null,
|
||||
|
||||
@@ -45,6 +45,7 @@ public interface VisionGroup<out V : Vision> : Vision, VisionContainer<V> {
|
||||
// public val propertyName: Name
|
||||
//) : VisionEvent
|
||||
|
||||
@VisionBuilder
|
||||
public interface MutableVisionGroup<V : Vision> : VisionGroup<V>, MutableVision, MutableVisionContainer<V> {
|
||||
|
||||
/**
|
||||
@@ -123,7 +124,6 @@ public class SimpleVisionGroup : AbstractVision(), MutableVisionGroup<Vision> {
|
||||
}
|
||||
}
|
||||
|
||||
@VisionBuilder
|
||||
public inline fun MutableVisionContainer<Vision>.group(
|
||||
name: NameToken? = null,
|
||||
builder: SimpleVisionGroup.() -> Unit = {},
|
||||
@@ -134,7 +134,6 @@ public inline fun MutableVisionContainer<Vision>.group(
|
||||
/**
|
||||
* Define a group with given [token], attach it to this parent and return it.
|
||||
*/
|
||||
@VisionBuilder
|
||||
public inline fun MutableVisionContainer<Vision>.group(
|
||||
token: String,
|
||||
builder: SimpleVisionGroup.() -> Unit = {},
|
||||
|
||||
@@ -27,7 +27,6 @@ public class VisionOfHtmlForm(
|
||||
/**
|
||||
* Create a [VisionOfHtmlForm] and bind this form to the id
|
||||
*/
|
||||
@HtmlTagMarker
|
||||
public inline fun <T, C : TagConsumer<T>> C.visionOfForm(
|
||||
vision: VisionOfHtmlForm,
|
||||
action: String? = null,
|
||||
|
||||
@@ -9,19 +9,13 @@ import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.NameToken
|
||||
import space.kscience.dataforge.names.asName
|
||||
import space.kscience.dataforge.names.parseAsName
|
||||
import space.kscience.visionforge.Vision
|
||||
import space.kscience.visionforge.VisionManager
|
||||
import space.kscience.visionforge.*
|
||||
import space.kscience.visionforge.html.VisionTagConsumer.Companion.DEFAULT_VISION_NAME
|
||||
import space.kscience.visionforge.setAsRoot
|
||||
import space.kscience.visionforge.visionManager
|
||||
|
||||
@DslMarker
|
||||
public annotation class VisionDSL
|
||||
|
||||
/**
|
||||
* A placeholder object to attach inline vision builders.
|
||||
*/
|
||||
@VisionDSL
|
||||
@VisionBuilder
|
||||
public class VisionOutput(override val context: Context, public val name: Name) : ContextAware {
|
||||
public var meta: Meta = Meta.EMPTY
|
||||
|
||||
@@ -55,7 +49,7 @@ public fun VisionOutput.meta(metaRepr: MetaRepr) {
|
||||
/**
|
||||
* Modified [TagConsumer] that allows rendering output fragments and visions in them
|
||||
*/
|
||||
@VisionDSL
|
||||
@VisionBuilder
|
||||
public abstract class VisionTagConsumer<R>(
|
||||
private val root: TagConsumer<R>,
|
||||
public val visionManager: VisionManager,
|
||||
@@ -111,7 +105,6 @@ public abstract class VisionTagConsumer<R>(
|
||||
* Insert a vision in this HTML.
|
||||
* TODO replace by multi-receiver
|
||||
*/
|
||||
@VisionDSL
|
||||
public open fun <T> TagConsumer<T>.vision(
|
||||
name: Name? = null,
|
||||
buildOutput: VisionOutput.() -> Vision,
|
||||
@@ -125,13 +118,11 @@ public abstract class VisionTagConsumer<R>(
|
||||
/**
|
||||
* TODO to be replaced by multi-receiver
|
||||
*/
|
||||
@VisionDSL
|
||||
public fun <T> TagConsumer<T>.vision(
|
||||
name: String?,
|
||||
buildOutput: VisionOutput.() -> Vision,
|
||||
): T = vision(name?.parseAsName(), buildOutput)
|
||||
|
||||
@VisionDSL
|
||||
public open fun <T> TagConsumer<T>.vision(
|
||||
vision: Vision,
|
||||
name: Name? = null,
|
||||
|
||||
@@ -75,7 +75,6 @@ private fun <T> TagConsumer<T>.vision(
|
||||
* Insert a vision in this HTML.
|
||||
*/
|
||||
context(htmlContext: HtmlVisionContext)
|
||||
@VisionDSL
|
||||
public fun <T> TagConsumer<T>.vision(
|
||||
name: Name? = null,
|
||||
visionProvider: VisionOutput.() -> Vision,
|
||||
@@ -90,7 +89,6 @@ public fun <T> TagConsumer<T>.vision(
|
||||
* Insert a vision in this HTML.
|
||||
*/
|
||||
context(htmlContext: HtmlVisionContext)
|
||||
@VisionDSL
|
||||
public fun <T> TagConsumer<T>.vision(
|
||||
name: String?,
|
||||
visionProvider: VisionOutput.() -> Vision,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package space.kscience.visionforge.meta
|
||||
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.test.advanceUntilIdle
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import space.kscience.dataforge.context.Global
|
||||
import space.kscience.dataforge.context.request
|
||||
import space.kscience.dataforge.meta.int
|
||||
@@ -13,14 +13,14 @@ import space.kscience.dataforge.meta.set
|
||||
import space.kscience.visionforge.*
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
|
||||
internal class PropertyFlowTest {
|
||||
|
||||
private val manager = Global.request(VisionManager)
|
||||
|
||||
@Test
|
||||
fun testChildrenPropertyFlow() = runTest(timeout = 200.milliseconds) {
|
||||
fun testChildrenPropertyFlow(): Unit = runBlocking {
|
||||
|
||||
val parent = MutableVisionGroup(manager) {
|
||||
|
||||
properties {
|
||||
@@ -37,33 +37,30 @@ internal class PropertyFlowTest {
|
||||
|
||||
val child = parent.getVision("child") as MutableVisionGroup<*>
|
||||
|
||||
val changesFlow = child.flowProperty("test", inherited = true)
|
||||
launch {
|
||||
val changesFlow = child.flowProperty("test", inherited = true).stateIn(this)
|
||||
|
||||
val collectedValues = ArrayList<Int>(5)
|
||||
assertEquals(22, child.readProperty("test", true).int)
|
||||
|
||||
changesFlow.onEach {
|
||||
collectedValues.add(it.int!!)
|
||||
}.launchIn(backgroundScope)
|
||||
delay(10)
|
||||
assertEquals(22, changesFlow.value.int)
|
||||
|
||||
delay(1)
|
||||
assertEquals(22, child.readProperty("test", true).int)
|
||||
parent.properties["test1"] = 88 // another property
|
||||
|
||||
parent.properties["test1"] = 88 // another property
|
||||
child.properties.remove("test")
|
||||
|
||||
child.properties.remove("test")
|
||||
assertEquals(11, child.readProperty("test", true).int)
|
||||
delay(10)
|
||||
assertEquals(11, changesFlow.value.int)
|
||||
|
||||
delay(1)
|
||||
assertEquals(11, child.readProperty("test", true).int)
|
||||
parent.properties["test"] = 33
|
||||
assertEquals(33, child.readProperty("test", true).int)
|
||||
|
||||
parent.properties["test"] = 33
|
||||
delay(1)
|
||||
assertEquals(33, child.readProperty("test", true).int)
|
||||
delay(10)
|
||||
assertEquals(33, changesFlow.value.int)
|
||||
|
||||
advanceUntilIdle()
|
||||
//assertEquals(listOf(22, 11, 33), collectedValues)
|
||||
assertEquals(22, collectedValues.first())
|
||||
assertEquals(33, collectedValues.last())
|
||||
|
||||
println("finished")
|
||||
cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
## Artifact:
|
||||
|
||||
The Maven coordinates of this project are `space.kscience:visionforge-gdml:0.5.0`.
|
||||
The Maven coordinates of this project are `space.kscience:visionforge-gdml:0.5.1`.
|
||||
|
||||
**Gradle Kotlin DSL:**
|
||||
```kotlin
|
||||
@@ -16,6 +16,6 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("space.kscience:visionforge-gdml:0.5.0")
|
||||
implementation("space.kscience:visionforge-gdml:0.5.1")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -7,6 +7,8 @@ kscience {
|
||||
js {
|
||||
binaries.library()
|
||||
}
|
||||
// native()
|
||||
// wasmJs()
|
||||
dependencies {
|
||||
api(projects.visionforgeSolid)
|
||||
api("space.kscience:gdml:0.5.0")
|
||||
@@ -14,4 +16,9 @@ kscience {
|
||||
dependencies(jvmTest) {
|
||||
implementation(spclibs.logback.classic)
|
||||
}
|
||||
}
|
||||
|
||||
readme {
|
||||
// TODO remove into a separate library
|
||||
maturity = space.kscience.gradle.Maturity.DEPRECATED
|
||||
}
|
||||
@@ -6,7 +6,7 @@ Common visionforge jupyter module
|
||||
|
||||
## Artifact:
|
||||
|
||||
The Maven coordinates of this project are `space.kscience:visionforge-jupyter:0.5.0`.
|
||||
The Maven coordinates of this project are `space.kscience:visionforge-jupyter:0.5.1`.
|
||||
|
||||
**Gradle Kotlin DSL:**
|
||||
```kotlin
|
||||
@@ -16,6 +16,6 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("space.kscience:visionforge-jupyter:0.5.0")
|
||||
implementation("space.kscience:visionforge-jupyter:0.5.1")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -6,7 +6,7 @@ Jupyter api artifact including all common modules
|
||||
|
||||
## Artifact:
|
||||
|
||||
The Maven coordinates of this project are `space.kscience:visionforge-jupyter-common:0.5.0`.
|
||||
The Maven coordinates of this project are `space.kscience:visionforge-jupyter-common:0.5.1`.
|
||||
|
||||
**Gradle Kotlin DSL:**
|
||||
```kotlin
|
||||
@@ -16,6 +16,6 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("space.kscience:visionforge-jupyter-common:0.5.0")
|
||||
implementation("space.kscience:visionforge-jupyter-common:0.5.1")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -36,10 +36,11 @@ kscience {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//tasks.processJupyterApiResources {
|
||||
// libraryProducers = listOf("space.kscience.visionforge.jupyter.JupyterCommonIntegration")
|
||||
//}
|
||||
kotlinJupyter {
|
||||
integrations {
|
||||
producer("space.kscience.visionforge.jupyter.JupyterCommonIntegration")
|
||||
}
|
||||
}
|
||||
|
||||
readme {
|
||||
maturity = space.kscience.gradle.Maturity.EXPERIMENTAL
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
## Artifact:
|
||||
|
||||
The Maven coordinates of this project are `space.kscience:visionforge-markdown:0.5.0`.
|
||||
The Maven coordinates of this project are `space.kscience:visionforge-markdown:0.5.1`.
|
||||
|
||||
**Gradle Kotlin DSL:**
|
||||
```kotlin
|
||||
@@ -16,6 +16,6 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("space.kscience:visionforge-markdown:0.5.0")
|
||||
implementation("space.kscience:visionforge-markdown:0.5.1")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
## Artifact:
|
||||
|
||||
The Maven coordinates of this project are `space.kscience:visionforge-server:0.5.0`.
|
||||
The Maven coordinates of this project are `space.kscience:visionforge-server:0.5.1`.
|
||||
|
||||
**Gradle Kotlin DSL:**
|
||||
```kotlin
|
||||
@@ -16,6 +16,6 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("space.kscience:visionforge-server:0.5.0")
|
||||
implementation("space.kscience:visionforge-server:0.5.1")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
## Artifact:
|
||||
|
||||
The Maven coordinates of this project are `space.kscience:visionforge-solid:0.5.0`.
|
||||
The Maven coordinates of this project are `space.kscience:visionforge-solid:0.5.1`.
|
||||
|
||||
**Gradle Kotlin DSL:**
|
||||
```kotlin
|
||||
@@ -16,6 +16,6 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("space.kscience:visionforge-solid:0.5.0")
|
||||
implementation("space.kscience:visionforge-solid:0.5.1")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -8,7 +8,7 @@ kscience {
|
||||
jvm()
|
||||
js()
|
||||
native()
|
||||
// wasm()
|
||||
wasmJs()
|
||||
useSerialization {
|
||||
json()
|
||||
}
|
||||
|
||||
@@ -84,7 +84,6 @@ public class Solids(meta: Meta) : VisionPlugin(meta), MutableVisionContainer<Sol
|
||||
}
|
||||
}
|
||||
|
||||
@VisionBuilder
|
||||
public inline fun VisionOutput.solid(options: Canvas3DOptions? = null, block: SolidGroup.() -> Unit): SolidGroup {
|
||||
requirePlugin(Solids)
|
||||
options?.let {
|
||||
@@ -97,6 +96,5 @@ public inline fun VisionOutput.solid(options: Canvas3DOptions? = null, block: So
|
||||
}
|
||||
}
|
||||
|
||||
@VisionBuilder
|
||||
public inline fun VisionOutput.solid(options: Canvas3DOptions.() -> Unit, block: SolidGroup.() -> Unit): SolidGroup =
|
||||
solid(Canvas3DOptions(options), block)
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
## Artifact:
|
||||
|
||||
The Maven coordinates of this project are `space.kscience:visionforge-tables:0.5.0`.
|
||||
The Maven coordinates of this project are `space.kscience:visionforge-tables:0.5.1`.
|
||||
|
||||
**Gradle Kotlin DSL:**
|
||||
```kotlin
|
||||
@@ -16,6 +16,6 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("space.kscience:visionforge-tables:0.5.0")
|
||||
implementation("space.kscience:visionforge-tables:0.5.1")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -19,6 +19,8 @@ kscience {
|
||||
}
|
||||
}
|
||||
}
|
||||
native()
|
||||
wasmJs()
|
||||
|
||||
useSerialization()
|
||||
commonMain {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
## Artifact:
|
||||
|
||||
The Maven coordinates of this project are `space.kscience:visionforge-threejs:0.5.0`.
|
||||
The Maven coordinates of this project are `space.kscience:visionforge-threejs:0.5.1`.
|
||||
|
||||
**Gradle Kotlin DSL:**
|
||||
```kotlin
|
||||
@@ -16,6 +16,6 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("space.kscience:visionforge-threejs:0.5.0")
|
||||
implementation("space.kscience:visionforge-threejs:0.5.1")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
## Artifact:
|
||||
|
||||
The Maven coordinates of this project are `space.kscience:visionforge-threejs-server:0.5.0`.
|
||||
The Maven coordinates of this project are `space.kscience:visionforge-threejs-server:0.5.1`.
|
||||
|
||||
**Gradle Kotlin DSL:**
|
||||
```kotlin
|
||||
@@ -16,6 +16,6 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("space.kscience:visionforge-threejs-server:0.5.0")
|
||||
implementation("space.kscience:visionforge-threejs-server:0.5.1")
|
||||
}
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user