[WIP] refactor inner logic
This commit is contained in:
parent
50bff2b729
commit
7d3078879d
3
.gitignore
vendored
3
.gitignore
vendored
@ -3,10 +3,9 @@
|
||||
*.iws
|
||||
out/
|
||||
.gradle
|
||||
.kotlin
|
||||
.kotlin/
|
||||
build/
|
||||
data/
|
||||
.kotlin/
|
||||
|
||||
jcef-bundle/
|
||||
|
||||
|
@ -10,7 +10,7 @@ val dataforgeVersion by extra("0.9.0")
|
||||
|
||||
allprojects {
|
||||
group = "space.kscience"
|
||||
version = "0.4.2"
|
||||
version = "0.5.0-dev-1"
|
||||
}
|
||||
|
||||
subprojects {
|
||||
@ -37,6 +37,7 @@ subprojects {
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
ksciencePublish {
|
||||
pom("https://github.com/SciProgCentre/visionforge") {
|
||||
useApache2Licence()
|
||||
|
@ -399,14 +399,14 @@ private fun SolidGroup.addRootVolume(
|
||||
|
||||
if (!cache) {
|
||||
val group = buildVolume(volume, context)?.apply(block) ?: return
|
||||
setChild(combinedName, group)
|
||||
setVision(combinedName, group)
|
||||
} else {
|
||||
val templateName = volumesName + volume.name
|
||||
val existing = context.prototypeHolder.getPrototype(templateName)
|
||||
if (existing == null) {
|
||||
context.prototypeHolder.prototypes {
|
||||
val group = buildVolume(volume, context) ?: return@prototypes
|
||||
setChild(templateName, group)
|
||||
setVision(templateName, group)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,6 @@ import space.kscience.dataforge.meta.string
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.gdml.GdmlShowCase
|
||||
import space.kscience.visionforge.Vision
|
||||
import space.kscience.visionforge.getChild
|
||||
import space.kscience.visionforge.solid.Solid
|
||||
import space.kscience.visionforge.solid.SolidMaterial
|
||||
import space.kscience.visionforge.solid.material
|
||||
|
@ -61,7 +61,7 @@ class Model(val manager: VisionManager) {
|
||||
}
|
||||
}
|
||||
|
||||
setChild("tracks".asName(), tracks)
|
||||
setVision("tracks".asName(), tracks)
|
||||
}
|
||||
|
||||
private fun highlight(pixel: String) {
|
||||
|
@ -6,7 +6,6 @@ import space.kscience.dataforge.meta.number
|
||||
import space.kscience.dataforge.names.asName
|
||||
import space.kscience.dataforge.names.startsWith
|
||||
import space.kscience.visionforge.onPropertyChange
|
||||
import space.kscience.visionforge.setChild
|
||||
import space.kscience.visionforge.solid.SolidGroup
|
||||
import space.kscience.visionforge.solid.SolidMaterial.Companion.EDGES_KEY
|
||||
import space.kscience.visionforge.solid.layer
|
||||
@ -21,7 +20,7 @@ internal fun SolidGroup.varBox(
|
||||
ySize: Number,
|
||||
name: String = "",
|
||||
action: VariableBox.() -> Unit = {},
|
||||
): VariableBox = VariableBox(xSize, ySize).apply(action).also { setChild(name, it) }
|
||||
): VariableBox = VariableBox(xSize, ySize).apply(action).also { setVision(name, it) }
|
||||
|
||||
internal class VariableBox(val xSize: Number, val ySize: Number) : ThreeJsVision() {
|
||||
|
||||
|
@ -16,6 +16,8 @@ kotlin {
|
||||
sourceSets {
|
||||
jvmMain {
|
||||
dependencies {
|
||||
implementation(projects.plotly.plotlyktCore)
|
||||
|
||||
implementation(compose.runtime)
|
||||
implementation(compose.foundation)
|
||||
implementation(compose.material)
|
||||
|
@ -11,6 +11,7 @@ import space.kscience.plotly.models.Scatter
|
||||
import space.kscience.plotly.models.invoke
|
||||
import space.kscience.plotly.server.pushUpdates
|
||||
import space.kscience.plotly.server.serve
|
||||
import space.kscience.visionforge.html.makeString
|
||||
import kotlin.math.PI
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
@ -25,15 +26,17 @@ fun staticPlot(): String = Plotly.page {
|
||||
val trace2 = Scatter(x, y2) {
|
||||
name = "cos"
|
||||
}
|
||||
plot(config = PlotlyConfig { responsive = true }) {//static plot
|
||||
traces(trace1, trace2)
|
||||
layout {
|
||||
title = "First graph, row: 1, size: 8/12"
|
||||
xaxis.title = "x axis name"
|
||||
yaxis { title = "y axis name" }
|
||||
vision {
|
||||
plotly(config = PlotlyConfig { responsive = true }) {//static plot
|
||||
traces(trace1, trace2)
|
||||
layout {
|
||||
title = "First graph, row: 1, size: 8/12"
|
||||
xaxis.title = "x axis name"
|
||||
yaxis { title = "y axis name" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}.render()
|
||||
}.makeString()
|
||||
|
||||
fun CoroutineScope.servePlots(scale: StateFlow<Number>): ApplicationEngine = Plotly.serve(this, port = 7778) {
|
||||
page("Static") { container ->
|
||||
|
34
plotly/plotly-kt-server/build.gradle.kts
Normal file
34
plotly/plotly-kt-server/build.gradle.kts
Normal file
@ -0,0 +1,34 @@
|
||||
|
||||
plugins {
|
||||
id("space.kscience.gradle.mpp")
|
||||
alias(spclibs.plugins.compose.compiler)
|
||||
alias(spclibs.plugins.compose.jb)
|
||||
}
|
||||
|
||||
val ktorVersion: String by rootProject.extra
|
||||
|
||||
kscience {
|
||||
fullStack(
|
||||
bundleName = "js/plotly-kt-server.js",
|
||||
browserConfig = {
|
||||
webpackTask {
|
||||
cssSupport {
|
||||
enabled = true
|
||||
}
|
||||
scssSupport {
|
||||
enabled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
commonMain {
|
||||
api(projects.visionforgeComposeHtml)
|
||||
api(projects.plotly.plotlyktCore)
|
||||
}
|
||||
|
||||
jvmMain {
|
||||
api(projects.visionforgeServer)
|
||||
implementation("io.ktor:ktor-server-cio")
|
||||
}
|
||||
}
|
11
plotly/plotly-kt-server/src/jsMain/kotlin/jsMain.kt
Normal file
11
plotly/plotly-kt-server/src/jsMain/kotlin/jsMain.kt
Normal file
@ -0,0 +1,11 @@
|
||||
package space.kscience.visionforge.plotly
|
||||
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.plotly.PlotlyPlugin
|
||||
import space.kscience.visionforge.html.runVisionClient
|
||||
|
||||
|
||||
@DFExperimental
|
||||
public fun main(): Unit = runVisionClient {
|
||||
plugin(PlotlyPlugin)
|
||||
}
|
79
plotly/plotly-kt-server/src/jvmMain/kotlin/serve.kt
Normal file
79
plotly/plotly-kt-server/src/jvmMain/kotlin/serve.kt
Normal file
@ -0,0 +1,79 @@
|
||||
package space.kscience.visionforge.plotly
|
||||
|
||||
import io.ktor.server.cio.CIO
|
||||
import io.ktor.server.engine.ApplicationEngine
|
||||
import io.ktor.server.engine.embeddedServer
|
||||
import io.ktor.server.http.content.staticResources
|
||||
import io.ktor.server.routing.routing
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.html.title
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.plotly.Plot
|
||||
import space.kscience.plotly.Plotly
|
||||
import space.kscience.plotly.PlotlyConfig
|
||||
import space.kscience.plotly.PlotlyPlugin
|
||||
import space.kscience.visionforge.html.HtmlFragment
|
||||
import space.kscience.visionforge.html.HtmlVisionFragment
|
||||
import space.kscience.visionforge.html.VisionPage
|
||||
import space.kscience.visionforge.server.visionPage
|
||||
import space.kscience.visionforge.setAsRoot
|
||||
|
||||
|
||||
public val VisionPage.Companion.plotlyKtHeader: HtmlFragment get() = scriptHeader("js/plotly-kt-server.js")
|
||||
|
||||
|
||||
@DFExperimental
|
||||
public fun Plotly.servePage(
|
||||
title: String = "VisionForge Plotly page",
|
||||
port: Int = 7777,
|
||||
visionPage: HtmlVisionFragment
|
||||
): ApplicationEngine = embeddedServer(CIO, port = port) {
|
||||
routing {
|
||||
staticResources("js", "js", null)
|
||||
}
|
||||
|
||||
visionPage(
|
||||
plugin.visionManager,
|
||||
HtmlFragment { title(title) },
|
||||
VisionPage.plotlyKtHeader,
|
||||
visionFragment = visionPage
|
||||
)
|
||||
|
||||
}.start(false)
|
||||
|
||||
|
||||
/**
|
||||
* Serve a page containing a single plot
|
||||
*/
|
||||
@DFExperimental
|
||||
public fun Plotly.serve(
|
||||
title: String = "VisionForge Plotly page",
|
||||
port: Int = 7777,
|
||||
config: PlotlyConfig = PlotlyConfig(),
|
||||
makePlot: suspend Plot.() -> Unit,
|
||||
): ApplicationEngine = embeddedServer(CIO, port = port) {
|
||||
routing {
|
||||
staticResources("js", "js", null)
|
||||
}
|
||||
|
||||
val plot: Plot = Plot()
|
||||
plot.setAsRoot(plugin.visionManager)
|
||||
|
||||
launch {
|
||||
makePlot(plot)
|
||||
}
|
||||
|
||||
visionPage(
|
||||
plugin.visionManager,
|
||||
HtmlFragment { title(title) },
|
||||
VisionPage.plotlyKtHeader,
|
||||
) {
|
||||
vision {
|
||||
requirePlugin(PlotlyPlugin)
|
||||
meta = config.meta
|
||||
plot
|
||||
}
|
||||
}
|
||||
|
||||
}.start(false)
|
||||
|
@ -1,9 +1,8 @@
|
||||
@file:OptIn(DFExperimental::class)
|
||||
|
||||
package space.kscience.plotly
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
@ -13,7 +12,6 @@ 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.misc.DFExperimental
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.NameToken
|
||||
import space.kscience.plotly.models.Layout
|
||||
@ -22,7 +20,7 @@ import space.kscience.visionforge.*
|
||||
|
||||
@Serializable
|
||||
public class VisionOfTrace(
|
||||
traceMeta: MutableMeta,
|
||||
private val traceMeta: MutableMeta,
|
||||
) : AbstractVision(traceMeta)
|
||||
|
||||
|
||||
@ -31,13 +29,14 @@ public class VisionOfTrace(
|
||||
*/
|
||||
@DFBuilder
|
||||
@Serializable
|
||||
public class Plot : AbstractVision(), VisionGroup {
|
||||
|
||||
private val traces = mutableListOf<VisionOfTrace>()
|
||||
public class Plot(
|
||||
private val traces: MutableList<VisionOfTrace> = mutableListOf<VisionOfTrace>(),
|
||||
) : AbstractVision(), VisionGroup {
|
||||
|
||||
@Transient
|
||||
private val traceFlow = MutableSharedFlow<Name>()
|
||||
|
||||
@Transient
|
||||
override val children: VisionChildren = object : VisionChildren {
|
||||
override val parent: Vision get() = this@Plot
|
||||
|
||||
@ -64,7 +63,10 @@ public class Plot : AbstractVision(), VisionGroup {
|
||||
public val layout: Layout by properties.root().scheme(Layout)
|
||||
|
||||
public fun addTrace(trace: Trace) {
|
||||
traces.add(VisionOfTrace((trace)))
|
||||
traces.add(VisionOfTrace(trace.meta))
|
||||
manager?.context?.launch {
|
||||
traceFlow.emit()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -118,6 +120,11 @@ public fun Plot.onDataChange(owner: Any?, callback: (index: Int, trace: Trace, p
|
||||
callback(index, trace, name)
|
||||
}
|
||||
}
|
||||
manager?.context?.launch{
|
||||
children.changes.collect{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -2,17 +2,24 @@ package space.kscience.plotly
|
||||
|
||||
import kotlinx.serialization.json.JsonArray
|
||||
import kotlinx.serialization.json.buildJsonArray
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.context.ContextAware
|
||||
import space.kscience.dataforge.context.request
|
||||
import space.kscience.dataforge.meta.*
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.visionforge.VisionBuilder
|
||||
import space.kscience.visionforge.VisionManager
|
||||
import space.kscience.visionforge.html.HtmlFragment
|
||||
import space.kscience.visionforge.html.HtmlVisionFragment
|
||||
import space.kscience.visionforge.html.VisionOutput
|
||||
import space.kscience.visionforge.html.VisionPage
|
||||
import kotlin.js.JsName
|
||||
|
||||
/**
|
||||
* A namespace for utility functions
|
||||
*/
|
||||
@JsName("PlotlyKt")
|
||||
public object Plotly {
|
||||
public object Plotly : ContextAware {
|
||||
public const val VERSION: String = "2.24.1"
|
||||
|
||||
public const val PLOTLY_CDN: String = "https://cdn.plot.ly/plotly-${VERSION}.min.js"
|
||||
@ -22,6 +29,12 @@ public object Plotly {
|
||||
"x", "y", "z", "text", "hovertext", "close", "high", "low", "open", "locations", "lon", "lat", "ids"
|
||||
)
|
||||
|
||||
override val context: Context = Context("Plotly") {
|
||||
plugin(PlotlyPlugin)
|
||||
}
|
||||
|
||||
public val plugin: PlotlyPlugin = context.request(PlotlyPlugin)
|
||||
|
||||
public inline fun plot(block: Plot.() -> Unit): Plot = Plot().apply(block)
|
||||
}
|
||||
|
||||
@ -78,3 +91,8 @@ public inline fun VisionOutput.plotly(
|
||||
meta = config.meta
|
||||
return Plotly.plot(block)
|
||||
}
|
||||
|
||||
public fun Plotly.page(
|
||||
pageHeaders: Map<String, HtmlFragment> = emptyMap(),
|
||||
content: HtmlVisionFragment,
|
||||
): VisionPage = VisionPage(context.request(VisionManager), pageHeaders, content)
|
@ -8,7 +8,6 @@ import space.kscience.dataforge.context.PluginTag
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.asName
|
||||
import space.kscience.plotly.plot
|
||||
import space.kscience.visionforge.Vision
|
||||
import space.kscience.visionforge.VisionPlugin
|
||||
import space.kscience.visionforge.html.ElementVisionRenderer
|
||||
@ -23,12 +22,12 @@ public class PlotlyJSPlugin : VisionPlugin(), ElementVisionRenderer {
|
||||
override val visionSerializersModule: SerializersModule get() = plotlySerializersModule
|
||||
|
||||
override fun rateVision(vision: Vision): Int = when (vision) {
|
||||
is VisionOfPlotly -> ElementVisionRenderer.DEFAULT_RATING
|
||||
is Plot -> ElementVisionRenderer.DEFAULT_RATING
|
||||
else -> ElementVisionRenderer.ZERO_RATING
|
||||
}
|
||||
|
||||
override fun render(element: Element, name: Name, vision: Vision, meta: Meta) {
|
||||
val plot = (vision as? VisionOfPlotly)?.plot ?: error("VisionOfPlotly expected but ${vision::class} found")
|
||||
val plot = (vision as? Plot) ?: error("VisionOfPlotly expected but ${vision::class} found")
|
||||
val config = PlotlyConfig.read(meta)
|
||||
element.plot(config, plot)
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ public fun Plot.makeFile(
|
||||
// fragment(pageBuilder).makeFile(null, true)
|
||||
|
||||
/**
|
||||
* Select a file to save plot to using Swing form.
|
||||
* Select a file to save plot to using a Swing form.
|
||||
*/
|
||||
@UnstablePlotlyAPI
|
||||
public fun Plotly.selectFile(filter: FileNameExtensionFilter? = null): Path? {
|
||||
|
@ -1,21 +0,0 @@
|
||||
# Module plotlykt-jupyter
|
||||
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
## Artifact:
|
||||
|
||||
The Maven coordinates of this project are `space.kscience:plotlykt-jupyter:0.7.2`.
|
||||
|
||||
**Gradle Kotlin DSL:**
|
||||
```kotlin
|
||||
repositories {
|
||||
maven("https://repo.kotlin.link")
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("space.kscience:plotlykt-jupyter:0.7.2")
|
||||
}
|
||||
```
|
@ -1,18 +0,0 @@
|
||||
public final class space/kscience/plotly/PlotlyIntegration : org/jetbrains/kotlinx/jupyter/api/libraries/JupyterIntegration, space/kscience/plotly/PlotlyRenderer {
|
||||
public fun <init> ()V
|
||||
public fun onLoaded (Lorg/jetbrains/kotlinx/jupyter/api/libraries/JupyterIntegration$Builder;)V
|
||||
public fun renderPlot (Lkotlinx/html/FlowContent;Lspace/kscience/plotly/Plot;Ljava/lang/String;Lspace/kscience/plotly/PlotlyConfig;)Lspace/kscience/plotly/Plot;
|
||||
}
|
||||
|
||||
public final class space/kscience/plotly/PlotlyIntegrationKt {
|
||||
public static final fun getJupyter (Lspace/kscience/plotly/Plotly;)Lspace/kscience/plotly/PlotlyJupyterConfiguration;
|
||||
}
|
||||
|
||||
public final class space/kscience/plotly/PlotlyJupyterConfiguration {
|
||||
public static final field INSTANCE Lspace/kscience/plotly/PlotlyJupyterConfiguration;
|
||||
public final fun getLegacyMode ()Z
|
||||
public final fun lab ()Lspace/kscience/visionforge/html/HtmlFragment;
|
||||
public final fun notebook ()Lspace/kscience/visionforge/html/HtmlFragment;
|
||||
public final fun setLegacyMode (Z)V
|
||||
}
|
||||
|
@ -1,100 +0,0 @@
|
||||
public final class space/kscience/plotly/server/MetaChangeCollector {
|
||||
public fun <init> ()V
|
||||
public final fun collect (Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/Meta;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
public final fun read (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
}
|
||||
|
||||
public final class space/kscience/plotly/server/MetaChangeCollectorKt {
|
||||
public static final fun collectUpdates (Lspace/kscience/plotly/Plot;Ljava/lang/String;Lkotlinx/coroutines/CoroutineScope;I)Lkotlinx/coroutines/flow/Flow;
|
||||
}
|
||||
|
||||
public final class space/kscience/plotly/server/PlotlyServer : kotlinx/coroutines/CoroutineScope, space/kscience/dataforge/meta/Configurable {
|
||||
public static final field Companion Lspace/kscience/plotly/server/PlotlyServer$Companion;
|
||||
public static final field DEFAULT_PAGE Ljava/lang/String;
|
||||
public final fun getApplication ()Lio/ktor/server/application/Application;
|
||||
public fun getCoroutineContext ()Lkotlin/coroutines/CoroutineContext;
|
||||
public final fun getDataSourceHost ()Ljava/lang/String;
|
||||
public final fun getDataSourcePort ()Ljava/lang/Integer;
|
||||
public final fun getEmbedData ()Z
|
||||
public synthetic fun getMeta ()Lspace/kscience/dataforge/meta/MutableMeta;
|
||||
public fun getMeta ()Lspace/kscience/dataforge/meta/ObservableMutableMeta;
|
||||
public final fun getUpdateInterval ()I
|
||||
public final fun getUpdateMode ()Lspace/kscience/plotly/server/PlotlyUpdateMode;
|
||||
public final fun header (Lkotlin/jvm/functions/Function1;)V
|
||||
public final fun page (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Lkotlin/jvm/functions/Function2;)V
|
||||
public final fun page (Lspace/kscience/plotly/PlotlyFragment;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;)V
|
||||
public static synthetic fun page$default (Lspace/kscience/plotly/server/PlotlyServer;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)V
|
||||
public static synthetic fun page$default (Lspace/kscience/plotly/server/PlotlyServer;Lspace/kscience/plotly/PlotlyFragment;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;ILjava/lang/Object;)V
|
||||
public final fun setDataSourceHost (Ljava/lang/String;)V
|
||||
public final fun setDataSourcePort (Ljava/lang/Integer;)V
|
||||
public final fun setEmbedData (Z)V
|
||||
public final fun setUpdateInterval (I)V
|
||||
public final fun setUpdateMode (Lspace/kscience/plotly/server/PlotlyUpdateMode;)V
|
||||
}
|
||||
|
||||
public final class space/kscience/plotly/server/PlotlyServer$Companion {
|
||||
public final fun getUPDATE_INTERVAL_KEY ()Lspace/kscience/dataforge/names/Name;
|
||||
public final fun getUPDATE_MODE_KEY ()Lspace/kscience/dataforge/names/Name;
|
||||
}
|
||||
|
||||
public final class space/kscience/plotly/server/PlotlyServerConfiguration : space/kscience/dataforge/meta/Scheme {
|
||||
public static final field INSTANCE Lspace/kscience/plotly/server/PlotlyServerConfiguration;
|
||||
public final fun getLegacyMode ()Z
|
||||
public final fun getPort ()I
|
||||
public final fun getUpdateInterval ()I
|
||||
public final fun notebook ()Lspace/kscience/visionforge/html/HtmlFragment;
|
||||
public final fun setLegacyMode (Z)V
|
||||
public final fun setPort (I)V
|
||||
public final fun setUpdateInterval (I)V
|
||||
}
|
||||
|
||||
public final class space/kscience/plotly/server/PlotlyServerIntegration : org/jetbrains/kotlinx/jupyter/api/libraries/JupyterIntegration {
|
||||
public fun <init> ()V
|
||||
public final fun isServerStarted ()Z
|
||||
public fun onLoaded (Lorg/jetbrains/kotlinx/jupyter/api/libraries/JupyterIntegration$Builder;)V
|
||||
}
|
||||
|
||||
public final class space/kscience/plotly/server/PlotlyServerIntegrationKt {
|
||||
public static final fun getJupyter (Lspace/kscience/plotly/Plotly;)Lspace/kscience/plotly/server/PlotlyServerConfiguration;
|
||||
}
|
||||
|
||||
public final class space/kscience/plotly/server/PlotlyServerKt {
|
||||
public static final fun close (Lio/ktor/server/engine/ApplicationEngine;)V
|
||||
public static final fun plot (Lspace/kscience/plotly/server/PlotlyServer;Ljava/lang/String;Lspace/kscience/plotly/PlotlyConfig;Lkotlin/jvm/functions/Function1;)V
|
||||
public static synthetic fun plot$default (Lspace/kscience/plotly/server/PlotlyServer;Ljava/lang/String;Lspace/kscience/plotly/PlotlyConfig;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
|
||||
public static final fun plotlyModule (Lio/ktor/server/application/Application;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)Lspace/kscience/plotly/server/PlotlyServer;
|
||||
public static synthetic fun plotlyModule$default (Lio/ktor/server/application/Application;Ljava/lang/String;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lspace/kscience/plotly/server/PlotlyServer;
|
||||
public static final fun pullUpdates (Lspace/kscience/plotly/server/PlotlyServer;I)Lspace/kscience/plotly/server/PlotlyServer;
|
||||
public static synthetic fun pullUpdates$default (Lspace/kscience/plotly/server/PlotlyServer;IILjava/lang/Object;)Lspace/kscience/plotly/server/PlotlyServer;
|
||||
public static final fun pushUpdates (Lspace/kscience/plotly/server/PlotlyServer;I)Lspace/kscience/plotly/server/PlotlyServer;
|
||||
public static synthetic fun pushUpdates$default (Lspace/kscience/plotly/server/PlotlyServer;IILjava/lang/Object;)Lspace/kscience/plotly/server/PlotlyServer;
|
||||
public static final fun serve (Lspace/kscience/plotly/Plotly;Lkotlinx/coroutines/CoroutineScope;Ljava/lang/String;ILkotlin/jvm/functions/Function1;)Lio/ktor/server/engine/ApplicationEngine;
|
||||
public static synthetic fun serve$default (Lspace/kscience/plotly/Plotly;Lkotlinx/coroutines/CoroutineScope;Ljava/lang/String;ILkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lio/ktor/server/engine/ApplicationEngine;
|
||||
public static final fun show (Lio/ktor/server/engine/ApplicationEngine;)V
|
||||
}
|
||||
|
||||
public final class space/kscience/plotly/server/PlotlyUpdateMode : java/lang/Enum {
|
||||
public static final field NONE Lspace/kscience/plotly/server/PlotlyUpdateMode;
|
||||
public static final field PULL Lspace/kscience/plotly/server/PlotlyUpdateMode;
|
||||
public static final field PUSH Lspace/kscience/plotly/server/PlotlyUpdateMode;
|
||||
public static fun getEntries ()Lkotlin/enums/EnumEntries;
|
||||
public static fun valueOf (Ljava/lang/String;)Lspace/kscience/plotly/server/PlotlyUpdateMode;
|
||||
public static fun values ()[Lspace/kscience/plotly/server/PlotlyUpdateMode;
|
||||
}
|
||||
|
||||
public abstract class space/kscience/plotly/server/Update {
|
||||
public synthetic fun <init> (Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
public final fun getId ()Ljava/lang/String;
|
||||
public abstract fun toJson ()Lkotlinx/serialization/json/JsonObject;
|
||||
}
|
||||
|
||||
public final class space/kscience/plotly/server/Update$Layout : space/kscience/plotly/server/Update {
|
||||
public fun <init> (Ljava/lang/String;Lspace/kscience/dataforge/meta/Meta;)V
|
||||
public fun toJson ()Lkotlinx/serialization/json/JsonObject;
|
||||
}
|
||||
|
||||
public final class space/kscience/plotly/server/Update$Trace : space/kscience/plotly/server/Update {
|
||||
public fun <init> (Ljava/lang/String;ILspace/kscience/dataforge/meta/Meta;)V
|
||||
public fun toJson ()Lkotlinx/serialization/json/JsonObject;
|
||||
}
|
||||
|
@ -62,9 +62,10 @@ include(
|
||||
":visionforge-jupyter:visionforge-jupyter-common",
|
||||
":plotly",
|
||||
":plotly:plotlykt-core",
|
||||
":plotly:plotly-kt-server",
|
||||
":plotly:plotlykt-script",
|
||||
":plotly:examples",
|
||||
":plotly:examples:fx-demo",
|
||||
// ":plotly:examples:fx-demo",
|
||||
":plotly:examples:compose-demo",
|
||||
":plotly:examples:js-demo",
|
||||
":plotly:examples:native-demo"
|
||||
|
@ -76,7 +76,7 @@ public class ComposeVisionClient : AbstractPlugin(), VisionClient {
|
||||
//subscribe to a backwards events propagation for control visions
|
||||
if (vision is ControlVision) {
|
||||
LaunchedEffect(vision) {
|
||||
vision.controlEventFlow.collect {
|
||||
vision.eventFlow.collect {
|
||||
sendEvent(name, it)
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ kscience {
|
||||
useCoroutines()
|
||||
commonMain {
|
||||
api("space.kscience:dataforge-context:$dataforgeVersion")
|
||||
api("com.benasher44:uuid:0.8.4")
|
||||
api(spclibs.kotlinx.html)
|
||||
}
|
||||
jsMain {
|
||||
|
@ -1,25 +1,59 @@
|
||||
package space.kscience.visionforge
|
||||
|
||||
import kotlinx.serialization.*
|
||||
import space.kscience.dataforge.meta.MutableMeta
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.flow.onCompletion
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.serialization.EncodeDefault
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
import space.kscience.dataforge.meta.ObservableMutableMeta
|
||||
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||
|
||||
|
||||
@Serializable
|
||||
public abstract class AbstractVision(
|
||||
@SerialName("properties")
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
@EncodeDefault(EncodeDefault.Mode.NEVER)
|
||||
private val propertiesInternal: MutableMeta = MutableMeta()
|
||||
): Vision {
|
||||
override val properties: ObservableMutableMeta = ObservableMutableMeta(),
|
||||
) : Vision {
|
||||
|
||||
private val _eventFlow by lazy {
|
||||
val scope = manager?.context ?: error("Can't observe orphan vision")
|
||||
MutableSharedFlow<VisionEvent>().also { flow ->
|
||||
properties.onChange(flow) {
|
||||
scope.launch {
|
||||
flow.emit(VisionPropertyChangedEvent(this@AbstractVision, it))
|
||||
}
|
||||
}
|
||||
|
||||
flow.onCompletion {
|
||||
properties.removeListener(flow)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected fun emitEvent(event: VisionEvent) {
|
||||
manager?.context?.launch {
|
||||
_eventFlow.emit(event)
|
||||
}
|
||||
}
|
||||
|
||||
override val eventFlow: SharedFlow<VisionEvent> get() = _eventFlow
|
||||
|
||||
@Transient
|
||||
override var parent: Vision? = null
|
||||
|
||||
|
||||
final override val properties: MutableVisionProperties by lazy {
|
||||
AbstractVisionProperties(this, propertiesInternal)
|
||||
}
|
||||
set(value) {
|
||||
if (value == null) {
|
||||
error("Can't remove parent for existing vision")
|
||||
} else if (field != null) {
|
||||
error("Parent is already set")
|
||||
} else {
|
||||
field = value
|
||||
}
|
||||
}
|
||||
|
||||
override val descriptor: MetaDescriptor? get() = null
|
||||
|
||||
}
|
@ -2,11 +2,12 @@ package space.kscience.visionforge
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.flow.filterIsInstance
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
import space.kscience.dataforge.meta.*
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.parseAsName
|
||||
@ -20,8 +21,6 @@ public abstract class VisionControlEvent : VisionEvent, MetaRepr {
|
||||
}
|
||||
|
||||
public interface ControlVision : Vision {
|
||||
public val controlEventFlow: SharedFlow<VisionControlEvent>
|
||||
|
||||
/**
|
||||
* Fire a [VisionControlEvent] on this [ControlVision]
|
||||
*/
|
||||
@ -45,14 +44,8 @@ public fun ControlVision.asyncControlEvent(
|
||||
@Serializable
|
||||
public abstract class AbstractControlVision : AbstractVision(), ControlVision {
|
||||
|
||||
@Transient
|
||||
private val mutableControlEventFlow = MutableSharedFlow<VisionControlEvent>()
|
||||
|
||||
override val controlEventFlow: SharedFlow<VisionControlEvent>
|
||||
get() = mutableControlEventFlow
|
||||
|
||||
override suspend fun dispatchControlEvent(event: VisionControlEvent) {
|
||||
mutableControlEventFlow.emit(event)
|
||||
emitEvent(event)
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,7 +84,7 @@ public interface DataControl : ControlVision {
|
||||
* Register listener
|
||||
*/
|
||||
public fun DataControl.onSubmit(scope: CoroutineScope, block: suspend VisionSubmitEvent.() -> Unit): Job =
|
||||
controlEventFlow.filterIsInstance<VisionSubmitEvent>().onEach(block).launchIn(scope)
|
||||
eventFlow.filterIsInstance<VisionSubmitEvent>().onEach(block).launchIn(scope)
|
||||
|
||||
|
||||
@Serializable
|
||||
|
@ -11,9 +11,9 @@ import kotlin.jvm.JvmInline
|
||||
* A container for styles
|
||||
*/
|
||||
@JvmInline
|
||||
public value class StyleSheet(private val owner: Vision) {
|
||||
public value class StyleSheet(private val owner: MutableVision) {
|
||||
|
||||
private val styleNode: Meta get() = owner.properties[STYLESHEET_KEY]
|
||||
private val styleNode: Meta get() = owner.properties[STYLESHEET_KEY] ?: Meta.EMPTY
|
||||
|
||||
public val items: Map<NameToken, Meta> get() = styleNode.items
|
||||
|
||||
@ -58,8 +58,10 @@ internal fun Vision.styleChanged(key: String, oldStyle: Meta?, newStyle: Meta?)
|
||||
.map { it.asName() }
|
||||
tokens.forEach { parent?.properties?.invalidate(it) }
|
||||
}
|
||||
children?.forEach { _, vision ->
|
||||
vision.styleChanged(key, oldStyle, newStyle)
|
||||
if(this is VisionGroup<*>) {
|
||||
children?.forEach { _, vision ->
|
||||
vision.styleChanged(key, oldStyle, newStyle)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -76,15 +78,15 @@ public var Vision.styles: List<String>
|
||||
* A stylesheet for this group and its descendants. Stylesheet is not applied directly,
|
||||
* but instead is just a repository for named configurations.
|
||||
*/
|
||||
public val Vision.styleSheet: StyleSheet get() = StyleSheet(this)
|
||||
public val MutableVision.styleSheet: StyleSheet get() = StyleSheet(this)
|
||||
|
||||
/**
|
||||
* Add style name to the list of styles to be resolved later.
|
||||
* The style with given name does not necessary exist at the moment.
|
||||
*/
|
||||
public fun Vision.useStyle(name: String, notify: Boolean = true) {
|
||||
val newStyle = properties.own[Vision.STYLE_KEY]?.value?.list?.plus(name.asValue()) ?: listOf(name.asValue())
|
||||
properties.setValue(Vision.STYLE_KEY, newStyle.asValue(), notify)
|
||||
public fun MutableVision.useStyle(name: String) {
|
||||
val newStyle = properties[Vision.STYLE_KEY]?.value?.list?.plus(name.asValue()) ?: listOf(name.asValue())
|
||||
properties.setValue(Vision.STYLE_KEY, newStyle.asValue())
|
||||
}
|
||||
|
||||
|
||||
@ -92,7 +94,7 @@ public fun Vision.useStyle(name: String, notify: Boolean = true) {
|
||||
* Resolve a style with given name for given [Vision]. The style is not necessarily applied to this [Vision].
|
||||
*/
|
||||
public fun Vision.getStyle(name: String): Meta? =
|
||||
properties.own[StyleSheet.STYLESHEET_KEY + name] ?: parent?.getStyle(name)
|
||||
properties[StyleSheet.STYLESHEET_KEY + name] ?: parent?.getStyle(name)
|
||||
|
||||
/**
|
||||
* Resolve a property from all styles
|
||||
|
@ -1,7 +1,10 @@
|
||||
package space.kscience.visionforge
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import space.kscience.dataforge.context.logger
|
||||
import space.kscience.dataforge.context.warn
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.MutableMeta
|
||||
import space.kscience.dataforge.meta.asValue
|
||||
import space.kscience.dataforge.meta.boolean
|
||||
import space.kscience.dataforge.meta.descriptors.Described
|
||||
@ -9,7 +12,7 @@ import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||
import space.kscience.dataforge.misc.DfType
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.asName
|
||||
import space.kscience.visionforge.AbstractVisionGroup.Companion.updateProperties
|
||||
import space.kscience.visionforge.SimpleVisionGroup.Companion.updateProperties
|
||||
import space.kscience.visionforge.Vision.Companion.TYPE
|
||||
|
||||
/**
|
||||
@ -23,36 +26,27 @@ public interface Vision : Described {
|
||||
*/
|
||||
public var parent: Vision?
|
||||
|
||||
override val descriptor: MetaDescriptor?
|
||||
|
||||
/**
|
||||
* Owner [VisionManager]. Used to define coroutine scope a serialization
|
||||
*/
|
||||
public val manager: VisionManager? get() = parent?.manager
|
||||
|
||||
|
||||
public val properties: MutableVisionProperties
|
||||
public val properties: Meta
|
||||
|
||||
/**
|
||||
* Update this vision using a dif represented by [VisionChange].
|
||||
* A flow of outgoing events from this vision
|
||||
*/
|
||||
public fun update(change: VisionChange) {
|
||||
if (change.children?.isNotEmpty() == true) {
|
||||
error("Vision is not a group")
|
||||
}
|
||||
change.properties?.let {
|
||||
updateProperties(it, Name.EMPTY)
|
||||
}
|
||||
}
|
||||
public val eventFlow: Flow<VisionEvent>
|
||||
|
||||
/**
|
||||
* Receive and process a generic [VisionEvent].
|
||||
*/
|
||||
public suspend fun receiveEvent(event: VisionEvent) {
|
||||
if(event is VisionChange) update(event)
|
||||
else manager?.logger?.warn { "Undispatched event: $event" }
|
||||
manager?.logger?.warn { "Undispatched event: $event" }
|
||||
}
|
||||
|
||||
override val descriptor: MetaDescriptor?
|
||||
|
||||
public companion object {
|
||||
public const val TYPE: String = "vision"
|
||||
public val STYLE_KEY: Name = "@style".asName()
|
||||
@ -62,10 +56,28 @@ public interface Vision : Described {
|
||||
}
|
||||
}
|
||||
|
||||
public interface MutableVision : Vision {
|
||||
override val properties: MutableMeta
|
||||
|
||||
/**
|
||||
* Receive and process a generic [VisionEvent].
|
||||
*/
|
||||
override suspend fun receiveEvent(event: VisionEvent) {
|
||||
if (event is VisionChange) {
|
||||
if (event.children?.isNotEmpty() == true) {
|
||||
error("Vision is not a group")
|
||||
}
|
||||
event.properties?.let {
|
||||
updateProperties(it, Name.EMPTY)
|
||||
}
|
||||
} else super.receiveEvent(event)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Control visibility of the element
|
||||
*/
|
||||
public var Vision.visible: Boolean?
|
||||
public var MutableVision.visible: Boolean?
|
||||
get() = properties.getValue(Vision.VISIBLE_KEY)?.boolean
|
||||
set(value) {
|
||||
properties.setValue(Vision.VISIBLE_KEY, value?.asValue())
|
||||
|
@ -1,12 +1,7 @@
|
||||
package space.kscience.visionforge
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.serialization.SerialName
|
||||
@ -19,7 +14,6 @@ import space.kscience.dataforge.names.plus
|
||||
import kotlin.time.Duration
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A vision used only in change propagation and showing that the target should be removed
|
||||
*/
|
||||
@ -32,7 +26,9 @@ public object NullVision : Vision {
|
||||
error("Can't set parent for null vision")
|
||||
}
|
||||
|
||||
override val properties: MutableVisionProperties get() = error("Can't get properties of `NullVision`")
|
||||
override val properties: Nothing get() = error("Can't get properties of `NullVision`")
|
||||
|
||||
override val eventFlow: Flow<VisionEvent> = emptyFlow()
|
||||
|
||||
override val descriptor: MetaDescriptor? = null
|
||||
}
|
||||
@ -65,6 +61,12 @@ public data class VisionChange(
|
||||
public val children: Map<Name, VisionChange>? = null,
|
||||
) : VisionEvent
|
||||
|
||||
///**
|
||||
// * A listener that listens to both current vision property changes and to children changes
|
||||
// */
|
||||
//public interface VisionGroupListener : VisionListener, MutableVisionContainer<Vision>
|
||||
|
||||
|
||||
/**
|
||||
* An update for a [Vision]
|
||||
*/
|
||||
@ -80,7 +82,11 @@ public class VisionChangeBuilder : MutableVisionContainer<Vision> {
|
||||
|
||||
@JvmSynchronized
|
||||
private fun getOrPutChild(visionName: Name): VisionChangeBuilder =
|
||||
children.getOrPut(visionName) { VisionChangeBuilder() }
|
||||
if (visionName.isEmpty()) {
|
||||
this
|
||||
} else {
|
||||
children.getOrPut(visionName) { VisionChangeBuilder() }
|
||||
}
|
||||
|
||||
@JvmSynchronized
|
||||
internal fun reset() {
|
||||
@ -102,13 +108,45 @@ public class VisionChangeBuilder : MutableVisionContainer<Vision> {
|
||||
}
|
||||
}
|
||||
|
||||
override fun setChild(name: Name?, child: Vision?) {
|
||||
if (name == null) error("Static children are not allowed in VisionChange")
|
||||
override fun setVision(name: Name, vision: Vision?) {
|
||||
getOrPutChild(name).apply {
|
||||
vision = child ?: NullVision
|
||||
this.vision = vision ?: NullVision
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateFrom(baseName: Name, change: VisionChange) {
|
||||
getOrPutChild(baseName).apply {
|
||||
change.vision?.let { this.vision = it }
|
||||
change.properties?.let { this.propertyChange.update(it) }
|
||||
change.children?.let { it.forEach { (key, change) -> updateFrom(baseName + key, change) } }
|
||||
}
|
||||
}
|
||||
|
||||
public fun consumeEvent(event: VisionEvent) {
|
||||
when (event) {
|
||||
is VisionChange -> updateFrom(Name.EMPTY, event)
|
||||
|
||||
is VisionPropertyChangedEvent -> propertyChanged(
|
||||
visionName = Name.EMPTY,
|
||||
propertyName = event.property,
|
||||
item = event.source.properties[event.property]
|
||||
)
|
||||
|
||||
is VisionGroupPropertyChangedEvent -> propertyChanged(
|
||||
visionName = event.childName,
|
||||
propertyName = event.propertyName,
|
||||
item = event.source.getVision(event.childName)?.properties?.get(event.propertyName)
|
||||
)
|
||||
|
||||
is VisionGroupCompositionChangedEvent -> setVision(event.name, event.source.getVision(event.name))
|
||||
is VisionControlEvent, is VisionMetaEvent -> {
|
||||
//do nothing
|
||||
//TODO add logging
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun build(visionManager: VisionManager): VisionChange = VisionChange(
|
||||
vision,
|
||||
if (propertyChange.isEmpty()) null else propertyChange,
|
||||
@ -136,40 +174,41 @@ public inline fun VisionManager.VisionChange(block: VisionChangeBuilder.() -> Un
|
||||
VisionChangeBuilder().apply(block).deepCopy(this)
|
||||
|
||||
|
||||
/**
|
||||
* Collect changes that are made to [source] to [collector] using [mutex] as a synchronization lock.
|
||||
*/
|
||||
private fun CoroutineScope.collectChange(
|
||||
name: Name,
|
||||
source: Vision,
|
||||
mutex: Mutex,
|
||||
collector: VisionChangeBuilder,
|
||||
) {
|
||||
|
||||
//Collect properties change
|
||||
source.properties.changes.onEach { propertyName ->
|
||||
val newItem = source.properties.own[propertyName]
|
||||
collector.propertyChanged(name, propertyName, newItem)
|
||||
}.launchIn(this)
|
||||
|
||||
val children = source.children
|
||||
//Subscribe for children changes
|
||||
children?.forEach { token, child ->
|
||||
collectChange(name + token, child, mutex, collector)
|
||||
}
|
||||
|
||||
//Subscribe for structure change
|
||||
children?.changes?.onEach { changedName ->
|
||||
val after = children[changedName]
|
||||
val fullName = name + changedName
|
||||
if (after != null) {
|
||||
collectChange(fullName, after, mutex, collector)
|
||||
}
|
||||
mutex.withLock {
|
||||
collector.setChild(fullName, after)
|
||||
}
|
||||
}?.launchIn(this)
|
||||
}
|
||||
///**
|
||||
// * Collect changes that are made to [source] to [collector] using [mutex] as a synchronization lock.
|
||||
// */
|
||||
//private fun CoroutineScope.collectChange(
|
||||
// name: Name,
|
||||
// source: Vision,
|
||||
// mutex: Mutex,
|
||||
// collector: VisionChangeBuilder,
|
||||
//) {
|
||||
//
|
||||
// source.listen(this, collector)
|
||||
// //Collect properties change
|
||||
// source.properties.changes.onEach { propertyName ->
|
||||
// val newItem = source.properties.own[propertyName]
|
||||
// collector.propertyChanged(name, propertyName, newItem)
|
||||
// }.launchIn(this)
|
||||
//
|
||||
// val children = source.children
|
||||
// //Subscribe for children changes
|
||||
// children?.forEach { token, child ->
|
||||
// collectChange(name + token, child, mutex, collector)
|
||||
// }
|
||||
//
|
||||
// //Subscribe for structure change
|
||||
// children?.changes?.onEach { changedName ->
|
||||
// val after = children[changedName]
|
||||
// val fullName = name + changedName
|
||||
// if (after != null) {
|
||||
// collectChange(fullName, after, mutex, collector)
|
||||
// }
|
||||
// mutex.withLock {
|
||||
// collector.setVision(fullName, after)
|
||||
// }
|
||||
// }?.launchIn(this)
|
||||
//}
|
||||
|
||||
/**
|
||||
* Generate a flow of changes of this vision and its children
|
||||
@ -184,7 +223,9 @@ public fun Vision.flowChanges(
|
||||
coroutineScope {
|
||||
val collector = VisionChangeBuilder()
|
||||
val mutex = Mutex()
|
||||
collectChange(Name.EMPTY, this@flowChanges, mutex, collector)
|
||||
eventFlow.onEach {
|
||||
collector.consumeEvent(it)
|
||||
}.launchIn(this)
|
||||
|
||||
if (sendInitial) {
|
||||
//Send initial vision state
|
||||
|
@ -1,11 +1,7 @@
|
||||
package space.kscience.visionforge
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import space.kscience.dataforge.names.*
|
||||
import space.kscience.visionforge.VisionChildren.Companion.STATIC_TOKEN_BODY
|
||||
import com.benasher44.uuid.uuid4
|
||||
import space.kscience.dataforge.names.Name
|
||||
|
||||
@DslMarker
|
||||
public annotation class VisionBuilder
|
||||
@ -15,7 +11,7 @@ public annotation class VisionBuilder
|
||||
* using DataForge [Name] objects as keys.
|
||||
*/
|
||||
public interface VisionContainer<out V : Vision> {
|
||||
public fun getChild(name: Name): V?
|
||||
public fun getVision(name: Name): V?
|
||||
}
|
||||
|
||||
/**
|
||||
@ -23,191 +19,9 @@ public interface VisionContainer<out V : Vision> {
|
||||
*/
|
||||
public interface MutableVisionContainer<in V : Vision> {
|
||||
//TODO add documentation
|
||||
public fun setChild(name: Name?, child: V?)
|
||||
}
|
||||
public fun setVision(name: Name, vision: V?)
|
||||
|
||||
/**
|
||||
* A serializable representation of [Vision] children container
|
||||
*/
|
||||
public interface VisionChildren : VisionContainer<Vision> {
|
||||
public val parent: Vision?
|
||||
|
||||
public val keys: Collection<NameToken>
|
||||
|
||||
public val values: Iterable<Vision> get() = keys.map { get(it)!! }
|
||||
|
||||
public val changes: Flow<Name>
|
||||
|
||||
public operator fun get(token: NameToken): Vision?
|
||||
|
||||
override fun getChild(name: Name): Vision? = when (name.length) {
|
||||
0 -> parent
|
||||
1 -> get(name.first())
|
||||
else -> get(name.first())?.children?.getChild(name.cutFirst())
|
||||
public companion object{
|
||||
public fun generateID(): String = "@vision[${uuid4().leastSignificantBits.toString(16)}]"
|
||||
}
|
||||
|
||||
public companion object {
|
||||
public const val STATIC_TOKEN_BODY: String = "@static"
|
||||
|
||||
public fun empty(owner: Vision): VisionChildren = object : VisionChildren {
|
||||
override val parent: Vision get() = owner
|
||||
override val keys: Set<NameToken> get() = emptySet()
|
||||
override val changes: Flow<Name> get() = emptyFlow()
|
||||
override fun get(token: NameToken): Vision? = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public operator fun VisionChildren.get(name: Name): Vision? = getChild(name)
|
||||
public operator fun VisionChildren.get(name: String): Vision? = getChild(name)
|
||||
|
||||
|
||||
public fun VisionChildren.isEmpty(): Boolean = keys.isEmpty()
|
||||
|
||||
public inline fun VisionChildren.forEach(block: (NameToken, Vision) -> Unit) {
|
||||
keys.forEach { block(it, get(it)!!) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A serializable representation of [Vision] children container
|
||||
* with the ability to modify the container content.
|
||||
*/
|
||||
public interface MutableVisionChildren : VisionChildren, MutableVisionContainer<Vision> {
|
||||
|
||||
public override val parent: MutableVisionGroup
|
||||
|
||||
public operator fun set(token: NameToken, value: Vision?)
|
||||
|
||||
/**
|
||||
* Set child [Vision] by name.
|
||||
* @param name child name. Pass null to add a static child. Note that static children cannot
|
||||
* be removed, replaced or accessed by name by other means.
|
||||
* @param child new child value. Pass null to delete the child.
|
||||
*/
|
||||
override fun setChild(name: Name?, child: Vision?) {
|
||||
when {
|
||||
name == null -> {
|
||||
if (child != null) {
|
||||
static(child)
|
||||
}
|
||||
}
|
||||
|
||||
name.isEmpty() -> error("Empty names are not allowed in VisionGroup::set")
|
||||
name.length == 1 -> {
|
||||
val token = name.tokens.first()
|
||||
set(token, child)
|
||||
}
|
||||
|
||||
else -> {
|
||||
val currentParent = get(name.first())
|
||||
if (currentParent != null && currentParent !is MutableVisionGroup) error("Can't assign a child to $currentParent")
|
||||
val parent: MutableVisionGroup = currentParent as? MutableVisionGroup ?: parent.createGroup().also {
|
||||
set(name.first(), it)
|
||||
}
|
||||
parent.children.setChild(name.cutFirst(), child)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public fun clear()
|
||||
}
|
||||
|
||||
public operator fun MutableVisionChildren.set(name: Name?, vision: Vision?) {
|
||||
setChild(name, vision)
|
||||
}
|
||||
|
||||
public operator fun MutableVisionChildren.set(name: String?, vision: Vision?) {
|
||||
setChild(name, vision)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a static child. Statics could not be found by name, removed or replaced. Changing statics also do not trigger events.
|
||||
*/
|
||||
public fun MutableVisionChildren.static(child: Vision) {
|
||||
set(NameToken(STATIC_TOKEN_BODY, index = child.hashCode().toString()), child)
|
||||
}
|
||||
|
||||
public fun VisionChildren.asSequence(): Sequence<Pair<NameToken, Vision>> = sequence {
|
||||
keys.forEach { yield(it to get(it)!!) }
|
||||
}
|
||||
|
||||
public operator fun VisionChildren.iterator(): Iterator<Pair<NameToken, Vision>> = asSequence().iterator()
|
||||
|
||||
public fun <V : Vision> VisionContainer<V>.getChild(str: String): V? = getChild(Name.parse(str))
|
||||
|
||||
public fun <V : Vision> MutableVisionContainer<V>.setChild(
|
||||
str: String?, vision: V?,
|
||||
): Unit = setChild(str?.parseAsName(), vision)
|
||||
|
||||
internal abstract class VisionChildrenImpl(
|
||||
override val parent: MutableVisionGroup,
|
||||
) : MutableVisionChildren {
|
||||
|
||||
private val updateJobs = HashMap<NameToken, Job>()
|
||||
|
||||
abstract var items: MutableMap<NameToken, Vision>?
|
||||
|
||||
@JvmSynchronized
|
||||
private fun buildItems(): MutableMap<NameToken, Vision> {
|
||||
if (items == null) {
|
||||
items = LinkedHashMap()
|
||||
}
|
||||
return items!!
|
||||
}
|
||||
|
||||
private val scope: CoroutineScope? get() = parent.manager?.context
|
||||
|
||||
override val keys: Set<NameToken> get() = items?.keys ?: emptySet()
|
||||
|
||||
override fun get(token: NameToken): Vision? = items?.get(token)
|
||||
|
||||
private val _changes = MutableSharedFlow<Name>()
|
||||
override val changes: SharedFlow<Name> get() = _changes
|
||||
|
||||
private fun onChange(name: Name) {
|
||||
scope?.launch {
|
||||
_changes.emit(name)
|
||||
}
|
||||
}
|
||||
|
||||
override operator fun set(token: NameToken, value: Vision?) {
|
||||
//fast return if value equals existing
|
||||
if (value == get(token)) return
|
||||
|
||||
val currentUpdateJob = updateJobs[token]
|
||||
if (currentUpdateJob != null) {
|
||||
currentUpdateJob.cancel()
|
||||
updateJobs.remove(token)
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
items?.remove(token)
|
||||
} else {
|
||||
(items ?: buildItems())[token] = value
|
||||
//check if parent already exists and is different from the current one
|
||||
if (value.parent != null && value.parent != parent) error("Can't reassign parent Vision for $value")
|
||||
//set parent
|
||||
value.parent = parent
|
||||
//start update jobs (only if the vision is rooted)
|
||||
scope?.let { scope ->
|
||||
val job = value.children?.changes?.onEach {
|
||||
onChange(token + it)
|
||||
}?.launchIn(scope)
|
||||
if (job != null) {
|
||||
updateJobs[token] = job
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onChange(token.asName())
|
||||
}
|
||||
|
||||
override fun clear() {
|
||||
items?.clear()
|
||||
updateJobs.values.forEach { it.cancel() }
|
||||
updateJobs.clear()
|
||||
onChange(Name.EMPTY)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -7,14 +7,19 @@ import space.kscience.dataforge.meta.MetaRepr
|
||||
import space.kscience.dataforge.names.Name
|
||||
|
||||
/**
|
||||
* An event propagated from client to a server
|
||||
* An event propagated from client to a server or vise versa
|
||||
*/
|
||||
public interface VisionEvent {
|
||||
public sealed interface VisionEvent {
|
||||
public companion object {
|
||||
public val CLICK_EVENT_KEY: Name get() = Name.of("events", "click", "payload")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An event that designates that property value is invalidated (not necessary changed
|
||||
*/
|
||||
public data class VisionPropertyChangedEvent(public val source: Vision, public val property: Name): VisionEvent
|
||||
|
||||
/**
|
||||
* An event that consists of custom meta
|
||||
*/
|
||||
|
@ -7,77 +7,106 @@ import space.kscience.dataforge.meta.ValueType
|
||||
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||
import space.kscience.dataforge.meta.descriptors.value
|
||||
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.dataforge.names.plus
|
||||
import space.kscience.visionforge.AbstractVisionGroup.Companion.updateProperties
|
||||
import space.kscience.visionforge.SimpleVisionGroup.Companion.updateProperties
|
||||
import space.kscience.visionforge.Vision.Companion.STYLE_KEY
|
||||
import kotlin.collections.component1
|
||||
import kotlin.collections.component2
|
||||
import kotlin.collections.set
|
||||
|
||||
|
||||
public interface VisionGroup : Vision {
|
||||
public val children: VisionChildren
|
||||
public interface VisionGroup<out V : Vision> : Vision, VisionContainer<V> {
|
||||
|
||||
override fun update(change: VisionChange) {
|
||||
change.children?.forEach { (name, change) ->
|
||||
if (change.vision != null) {
|
||||
error("VisionGroup is read-only")
|
||||
} else {
|
||||
children.getChild(name)?.update(change)
|
||||
override suspend fun receiveEvent(event: VisionEvent) {
|
||||
super.receiveEvent(event)
|
||||
if (event is VisionChange) {
|
||||
event.children?.forEach { (name, change) ->
|
||||
if (event.vision != null) {
|
||||
error("VisionGroup is read-only")
|
||||
} else {
|
||||
getVision(name)?.receiveEvent(change)
|
||||
}
|
||||
}
|
||||
}
|
||||
change.properties?.let {
|
||||
updateProperties(it, Name.EMPTY)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface MutableVisionGroup : VisionGroup {
|
||||
|
||||
override val children: MutableVisionChildren
|
||||
|
||||
public fun createGroup(): MutableVisionGroup
|
||||
|
||||
override fun update(change: VisionChange) {
|
||||
change.children?.forEach { (name, change) ->
|
||||
when {
|
||||
change.vision == NullVision -> children.setChild(name, null)
|
||||
change.vision != null -> children.setChild(name, change.vision)
|
||||
else -> children.getChild(name)?.update(change)
|
||||
}
|
||||
}
|
||||
change.properties?.let {
|
||||
updateProperties(it, Name.EMPTY)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public val Vision.children: VisionChildren? get() = (this as? VisionGroup)?.children
|
||||
|
||||
/**
|
||||
* A full base implementation for a [Vision]
|
||||
* An event that indicates that [VisionGroup] composition is invalidated (not necessarily changed
|
||||
*/
|
||||
@Serializable
|
||||
public abstract class AbstractVisionGroup : AbstractVision(), MutableVisionGroup {
|
||||
public data class VisionGroupCompositionChangedEvent(public val source: VisionGroup<*>, public val name: Name) :
|
||||
VisionEvent
|
||||
|
||||
@SerialName("children")
|
||||
protected var childrenInternal: MutableMap<NameToken, Vision>? = null
|
||||
///**
|
||||
// * An event that indicates that child property value has been invalidated
|
||||
// */
|
||||
//public data class VisionGroupPropertyChangedEvent(
|
||||
// public val source: VisionGroup<*>,
|
||||
// public val childName: Name,
|
||||
// public val propertyName: Name
|
||||
//) : VisionEvent
|
||||
|
||||
public interface MutableVisionGroup<V : Vision> : VisionGroup<V>, MutableVision, MutableVisionContainer<V> {
|
||||
|
||||
init {
|
||||
childrenInternal?.forEach { it.value.parent = this }
|
||||
}
|
||||
/**
|
||||
* This method tries to convert a [vision] to the typed vision handled by this [MutableVisionGroup].
|
||||
* Return null if conversion is failed.
|
||||
*/
|
||||
public fun convertVisionOrNull(vision: Vision): V?
|
||||
|
||||
override val children: MutableVisionChildren by lazy {
|
||||
object : VisionChildrenImpl(this) {
|
||||
override var items: MutableMap<NameToken, Vision>?
|
||||
get() = this@AbstractVisionGroup.childrenInternal
|
||||
set(value) {
|
||||
this@AbstractVisionGroup.childrenInternal = value
|
||||
override suspend fun receiveEvent(event: VisionEvent) {
|
||||
if (event is VisionChange) {
|
||||
event.properties?.let {
|
||||
updateProperties(it, Name.EMPTY)
|
||||
}
|
||||
event.children?.forEach { (name, change) ->
|
||||
change.children?.forEach { (name, change) ->
|
||||
when {
|
||||
change.vision == NullVision -> setVision(name, null)
|
||||
change.vision != null -> setVision(
|
||||
name,
|
||||
convertVisionOrNull(change.vision) ?: error("Can't convert ${change.vision}")
|
||||
)
|
||||
|
||||
else -> getVision(name)?.receiveEvent(change)
|
||||
}
|
||||
}
|
||||
change.properties?.let {
|
||||
updateProperties(it, Name.EMPTY)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract override fun createGroup(): AbstractVisionGroup
|
||||
/**
|
||||
* A simple vision group that just holds children. Nothing else.
|
||||
*/
|
||||
@Serializable
|
||||
@SerialName("vision.group")
|
||||
public class SimpleVisionGroup : AbstractVision(), MutableVisionGroup<Vision> {
|
||||
|
||||
@Serializable
|
||||
@SerialName("children")
|
||||
private val _children = mutableMapOf<Name, Vision>()
|
||||
|
||||
public val children: Map<Name, Vision> get() = _children
|
||||
|
||||
override fun convertVisionOrNull(vision: Vision): Vision = vision
|
||||
|
||||
override fun getVision(name: Name): Vision? = children[name]
|
||||
|
||||
override fun setVision(name: Name, vision: Vision?) {
|
||||
if (vision == null) {
|
||||
_children.remove(name)
|
||||
} else {
|
||||
_children[name] = vision
|
||||
vision.parent = this
|
||||
}
|
||||
emitEvent(VisionGroupCompositionChangedEvent(this, name))
|
||||
}
|
||||
|
||||
public companion object {
|
||||
public val descriptor: MetaDescriptor = MetaDescriptor {
|
||||
@ -86,7 +115,7 @@ public abstract class AbstractVisionGroup : AbstractVision(), MutableVisionGroup
|
||||
}
|
||||
}
|
||||
|
||||
public fun Vision.updateProperties(item: Meta, name: Name = Name.EMPTY) {
|
||||
public fun MutableVision.updateProperties(item: Meta, name: Name = Name.EMPTY) {
|
||||
properties.setValue(name, item.value)
|
||||
item.items.forEach { (token, item) ->
|
||||
updateProperties(item, name + token)
|
||||
@ -96,24 +125,13 @@ public abstract class AbstractVisionGroup : AbstractVision(), MutableVisionGroup
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple vision group that just holds children. Nothing else.
|
||||
*/
|
||||
@Serializable
|
||||
@SerialName("vision.group")
|
||||
public class SimpleVisionGroup : AbstractVisionGroup(), MutableVisionContainer<Vision> {
|
||||
override fun createGroup(): SimpleVisionGroup = SimpleVisionGroup()
|
||||
|
||||
override fun setChild(name: Name?, child: Vision?) {
|
||||
children.setChild(name, child)
|
||||
}
|
||||
}
|
||||
|
||||
@VisionBuilder
|
||||
public inline fun MutableVisionContainer<Vision>.group(
|
||||
name: Name? = null,
|
||||
builder: SimpleVisionGroup.() -> Unit = {},
|
||||
): SimpleVisionGroup = SimpleVisionGroup().also { setChild(name, it) }.apply(builder)
|
||||
): SimpleVisionGroup = SimpleVisionGroup().also {
|
||||
setVision(name ?: MutableVisionContainer.generateID().asName(), it)
|
||||
}.apply(builder)
|
||||
|
||||
/**
|
||||
* Define a group with given [name], attach it to this parent and return it.
|
||||
|
@ -15,7 +15,7 @@ import space.kscience.dataforge.meta.toMeta
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.visionforge.html.*
|
||||
|
||||
public class VisionManager(meta: Meta) : AbstractPlugin(meta), MutableVisionContainer<Vision> {
|
||||
public class VisionManager(meta: Meta) : AbstractPlugin(meta) {
|
||||
override val tag: PluginTag get() = Companion.tag
|
||||
|
||||
/**
|
||||
@ -54,7 +54,7 @@ public class VisionManager(meta: Meta) : AbstractPlugin(meta), MutableVisionCont
|
||||
public fun encodeToMeta(vision: Vision, descriptor: MetaDescriptor? = null): Meta =
|
||||
encodeToJsonElement(vision).toMeta(descriptor)
|
||||
|
||||
override fun setChild(name: Name?, child: Vision?) {
|
||||
override fun setVision(name: Name?, child: Vision?) {
|
||||
child?.setAsRoot(this)
|
||||
}
|
||||
|
||||
@ -124,7 +124,7 @@ public fun Vision.encodeToString(): String =
|
||||
/**
|
||||
* A root vision attached to [VisionManager]
|
||||
*/
|
||||
public class RootVision(override val manager: VisionManager) : AbstractVisionGroup() {
|
||||
public class RootVision(override val manager: VisionManager) : SimpleVisionGroup() {
|
||||
override fun createGroup(): SimpleVisionGroup = SimpleVisionGroup()
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,6 @@ import kotlinx.serialization.Transient
|
||||
import space.kscience.dataforge.meta.*
|
||||
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||
import space.kscience.dataforge.meta.descriptors.get
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.dataforge.names.*
|
||||
|
||||
public interface VisionProperties : MetaProvider {
|
||||
@ -103,82 +102,6 @@ public operator fun MutableVisionProperties.invoke(block: MutableMeta.() -> Unit
|
||||
root(inherit = false, includeStyles = false).apply(block)
|
||||
}
|
||||
|
||||
private class VisionPropertiesItem(
|
||||
val properties: MutableVisionProperties,
|
||||
val nodeName: Name,
|
||||
val inherit: Boolean? = null,
|
||||
val useStyles: Boolean? = null,
|
||||
val default: Meta? = null,
|
||||
) : MutableTypedMeta<VisionPropertiesItem> {
|
||||
|
||||
override val self: VisionPropertiesItem get() = this
|
||||
|
||||
|
||||
val descriptor: MetaDescriptor? by lazy { properties.descriptor?.get(nodeName) }
|
||||
|
||||
|
||||
override val items: Map<NameToken, VisionPropertiesItem>
|
||||
get() {
|
||||
val metaKeys = properties.own[nodeName]?.items?.keys ?: emptySet()
|
||||
val descriptorKeys = descriptor?.nodes?.map { NameToken(it.key) } ?: emptySet()
|
||||
val defaultKeys = default?.get(nodeName)?.items?.keys ?: emptySet()
|
||||
val inheritFlag = descriptor?.inherited ?: inherit
|
||||
val stylesFlag = descriptor?.usesStyles ?: useStyles
|
||||
return (metaKeys + descriptorKeys + defaultKeys).associateWith {
|
||||
VisionPropertiesItem(
|
||||
properties,
|
||||
nodeName + it,
|
||||
inheritFlag,
|
||||
stylesFlag,
|
||||
default
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override var value: Value?
|
||||
get() {
|
||||
val inheritFlag = descriptor?.inherited ?: inherit ?: false
|
||||
val stylesFlag = descriptor?.usesStyles ?: useStyles ?: true
|
||||
return properties.getValue(nodeName, inheritFlag, stylesFlag) ?: default?.getValue(nodeName)
|
||||
}
|
||||
set(value) {
|
||||
properties.setValue(nodeName, value)
|
||||
}
|
||||
|
||||
//TODO remove with DataForge 0.9.1
|
||||
override fun get(name: Name): VisionPropertiesItem? {
|
||||
tailrec fun VisionPropertiesItem.find(name: Name): VisionPropertiesItem? = if (name.isEmpty()) {
|
||||
this
|
||||
} else {
|
||||
items[name.firstOrNull()!!]?.find(name.cutFirst())
|
||||
}
|
||||
|
||||
return self.find(name)
|
||||
}
|
||||
|
||||
|
||||
@DFExperimental
|
||||
override fun attach(name: Name, node: VisionPropertiesItem) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun getOrCreate(name: Name): VisionPropertiesItem = VisionPropertiesItem(
|
||||
properties,
|
||||
nodeName + name,
|
||||
inherit,
|
||||
useStyles,
|
||||
default
|
||||
)
|
||||
|
||||
override fun set(name: Name, node: Meta?) {
|
||||
properties[nodeName + name] = node
|
||||
}
|
||||
|
||||
override fun toString(): String = Meta.toString(this)
|
||||
override fun equals(other: Any?): Boolean = Meta.equals(this, other as? Meta)
|
||||
override fun hashCode(): Int = Meta.hashCode(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* A base implementation of [MutableVisionProperties]
|
||||
*/
|
||||
|
@ -0,0 +1,140 @@
|
||||
package space.kscience.visionforge
|
||||
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.MutableMeta
|
||||
import space.kscience.dataforge.meta.Value
|
||||
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||
import space.kscience.dataforge.meta.descriptors.get
|
||||
import space.kscience.dataforge.names.*
|
||||
|
||||
internal class VisionProperty(
|
||||
val parent: Vision,
|
||||
val nodeName: Name,
|
||||
val inherit: Boolean,
|
||||
val useStyles: Boolean,
|
||||
val default: Meta? = null,
|
||||
) : Meta {
|
||||
|
||||
val descriptor: MetaDescriptor? by lazy { parent.descriptor?.get(nodeName) }
|
||||
|
||||
|
||||
override val items: Map<NameToken, VisionProperty>
|
||||
get() {
|
||||
val metaKeys = parent.properties[nodeName]?.items?.keys ?: emptySet()
|
||||
val descriptorKeys = descriptor?.nodes?.map { NameToken(it.key) } ?: emptySet()
|
||||
val defaultKeys = default?.get(nodeName)?.items?.keys ?: emptySet()
|
||||
return (metaKeys + descriptorKeys + defaultKeys).associateWith {
|
||||
VisionProperty(
|
||||
parent = parent,
|
||||
nodeName = nodeName + it,
|
||||
inherit = inherit,
|
||||
useStyles = useStyles,
|
||||
default = default
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override val value: Value?
|
||||
get() {
|
||||
return parent.getProperty(nodeName, inherit, useStyles).value ?: default?.getValue(nodeName)
|
||||
}
|
||||
|
||||
//TODO remove with DataForge 0.9.1
|
||||
override fun get(name: Name): VisionProperty? {
|
||||
tailrec fun VisionProperty.find(name: Name): VisionProperty? = if (name.isEmpty()) {
|
||||
this
|
||||
} else {
|
||||
items[name.firstOrNull()!!]?.find(name.cutFirst())
|
||||
}
|
||||
|
||||
return find(name)
|
||||
}
|
||||
|
||||
override fun toString(): String = Meta.toString(this)
|
||||
override fun equals(other: Any?): Boolean = Meta.equals(this, other as? Meta)
|
||||
override fun hashCode(): Int = Meta.hashCode(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a property, taking into account inherited properties and styles if necessary
|
||||
*/
|
||||
public fun Vision.getProperty(
|
||||
name: Name,
|
||||
inherit: Boolean = descriptor?.get(name)?.inherited ?: false,
|
||||
includeStyles: Boolean = descriptor?.get(name)?.usesStyles ?: true,
|
||||
): Meta = VisionProperty(this, name, inherit, includeStyles)
|
||||
|
||||
|
||||
|
||||
|
||||
internal class MutableVisionProperty(
|
||||
val parent: MutableVision,
|
||||
val nodeName: Name,
|
||||
val inherit: Boolean,
|
||||
val useStyles: Boolean,
|
||||
val default: Meta? = null,
|
||||
) : MutableMeta{
|
||||
|
||||
|
||||
val descriptor: MetaDescriptor? by lazy { parent.descriptor?.get(nodeName) }
|
||||
|
||||
|
||||
override val items: Map<NameToken, MutableVisionProperty>
|
||||
get() {
|
||||
val metaKeys = parent.properties[nodeName]?.items?.keys ?: emptySet()
|
||||
val descriptorKeys = descriptor?.nodes?.map { NameToken(it.key) } ?: emptySet()
|
||||
val defaultKeys = default?.get(nodeName)?.items?.keys ?: emptySet()
|
||||
return (metaKeys + descriptorKeys + defaultKeys).associateWith {
|
||||
MutableVisionProperty(
|
||||
parent = parent,
|
||||
nodeName = nodeName + it,
|
||||
inherit = inherit,
|
||||
useStyles = useStyles,
|
||||
default = default
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override var value: Value?
|
||||
get() {
|
||||
return parent.getProperty(nodeName, inherit, useStyles).value ?: default?.getValue(nodeName)
|
||||
}
|
||||
set(value) {
|
||||
parent.properties.setValue(nodeName, value)
|
||||
}
|
||||
|
||||
//TODO remove with DataForge 0.9.1
|
||||
override fun get(name: Name): MutableVisionProperty? {
|
||||
tailrec fun MutableVisionProperty.find(name: Name): MutableVisionProperty? = if (name.isEmpty()) {
|
||||
this
|
||||
} else {
|
||||
items[name.firstOrNull()!!]?.find(name.cutFirst())
|
||||
}
|
||||
|
||||
return find(name)
|
||||
}
|
||||
|
||||
|
||||
|
||||
override fun getOrCreate(name: Name): MutableVisionProperty = MutableVisionProperty(
|
||||
parent = parent,
|
||||
nodeName = nodeName + name,
|
||||
inherit = inherit,
|
||||
useStyles = useStyles,
|
||||
default = default
|
||||
)
|
||||
|
||||
override fun set(name: Name, node: Meta?) {
|
||||
parent.properties[nodeName + name] = node
|
||||
}
|
||||
|
||||
override fun toString(): String = Meta.toString(this)
|
||||
override fun equals(other: Any?): Boolean = Meta.equals(this, other as? Meta)
|
||||
override fun hashCode(): Int = Meta.hashCode(this)
|
||||
}
|
||||
|
||||
public fun MutableVision.getProperty(
|
||||
name: Name,
|
||||
inherit: Boolean = descriptor?.get(name)?.inherited ?: false,
|
||||
includeStyles: Boolean = descriptor?.get(name)?.usesStyles ?: true,
|
||||
): MutableMeta = MutableVisionProperty(this, name, inherit, includeStyles)
|
@ -88,12 +88,12 @@ public open class VisionOfHtmlInput(
|
||||
public fun VisionOfHtmlInput.onValueChange(
|
||||
scope: CoroutineScope = manager?.context ?: error("Coroutine context is not resolved for $this"),
|
||||
callback: suspend VisionValueChangeEvent.() -> Unit,
|
||||
): Job = controlEventFlow.filterIsInstance<VisionValueChangeEvent>().onEach(callback).launchIn(scope)
|
||||
): Job = eventFlow.filterIsInstance<VisionValueChangeEvent>().onEach(callback).launchIn(scope)
|
||||
|
||||
public fun VisionOfHtmlInput.onInput(
|
||||
scope: CoroutineScope = manager?.context ?: error("Coroutine context is not resolved for $this"),
|
||||
callback: suspend VisionInputEvent.() -> Unit,
|
||||
): Job = controlEventFlow.filterIsInstance<VisionInputEvent>().onEach(callback).launchIn(scope)
|
||||
): Job = eventFlow.filterIsInstance<VisionInputEvent>().onEach(callback).launchIn(scope)
|
||||
|
||||
@Suppress("UnusedReceiverParameter")
|
||||
public inline fun VisionOutput.htmlInput(
|
||||
|
@ -2,10 +2,10 @@ package space.kscience.visionforge
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.descriptors.get
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.asName
|
||||
import space.kscience.dataforge.names.parseAsName
|
||||
@ -13,45 +13,50 @@ import space.kscience.dataforge.names.startsWith
|
||||
import kotlin.reflect.KProperty1
|
||||
|
||||
|
||||
private fun Vision.inheritedEventFlow(): Flow<VisionEvent> =
|
||||
parent?.let { parent -> merge(eventFlow, parent.inheritedEventFlow()) } ?: eventFlow
|
||||
|
||||
/**
|
||||
* Call [callback] on initial value of the property and then on all subsequent values after change
|
||||
*/
|
||||
public fun Vision.useProperty(
|
||||
propertyName: Name,
|
||||
inherit: Boolean? = null,
|
||||
includeStyles: Boolean? = null,
|
||||
inherited: Boolean = descriptor?.get(propertyName)?.inherited ?: false,
|
||||
useStyles: Boolean = descriptor?.get(propertyName)?.usesStyles ?: true,
|
||||
scope: CoroutineScope = manager?.context ?: error("Orphan Vision can't observe properties. Use explicit scope."),
|
||||
callback: (Meta) -> Unit,
|
||||
): Job {
|
||||
//Pass initial value.
|
||||
callback(properties.get(propertyName, inherit, includeStyles))
|
||||
return properties.changes.onEach { name ->
|
||||
if (name.startsWith(propertyName)) {
|
||||
callback(properties.get(propertyName, inherit, includeStyles))
|
||||
callback: suspend (Meta) -> Unit,
|
||||
): Job = scope.launch {
|
||||
//Pass initial value synchronously
|
||||
|
||||
callback(getProperty(propertyName, inherited, useStyles))
|
||||
|
||||
val combinedFlow = if (inherited) {
|
||||
inheritedEventFlow()
|
||||
} else {
|
||||
eventFlow
|
||||
}
|
||||
|
||||
combinedFlow.filterIsInstance<VisionPropertyChangedEvent>().onEach {
|
||||
if (it.property == propertyName || (useStyles && it.property == Vision.STYLE_KEY)) {
|
||||
callback(getProperty(propertyName, inherited, useStyles))
|
||||
}
|
||||
}.launchIn(scope)
|
||||
}.collect()
|
||||
}
|
||||
|
||||
public fun Vision.useProperty(
|
||||
propertyName: String,
|
||||
inherit: Boolean? = null,
|
||||
includeStyles: Boolean? = null,
|
||||
inherited: Boolean = descriptor?.get(propertyName)?.inherited ?: false,
|
||||
useStyles: Boolean = descriptor?.get(propertyName)?.usesStyles ?: true,
|
||||
scope: CoroutineScope = manager?.context ?: error("Orphan Vision can't observe properties. Use explicit scope."),
|
||||
callback: (Meta) -> Unit,
|
||||
): Job = useProperty(propertyName.parseAsName(), inherit, includeStyles, scope, callback)
|
||||
callback: suspend (Meta) -> Unit,
|
||||
): Job = useProperty(propertyName.parseAsName(), inherited, useStyles, scope, callback)
|
||||
|
||||
public fun <V : Vision, T> V.useProperty(
|
||||
property: KProperty1<V, T>,
|
||||
scope: CoroutineScope = manager?.context ?: error("Orphan Vision can't observe properties. Use explicit scope."),
|
||||
callback: V.(T) -> Unit,
|
||||
): Job {
|
||||
//Pass initial value.
|
||||
callback: suspend V.(T) -> Unit,
|
||||
): Job = useProperty(property.name, scope = scope){
|
||||
callback(property.get(this))
|
||||
return properties.changes.onEach { name ->
|
||||
if (name.startsWith(property.name.asName())) {
|
||||
callback(property.get(this@useProperty))
|
||||
}
|
||||
}.launchIn(scope)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -185,7 +185,7 @@ public class JsVisionClient : AbstractPlugin(), VisionClient {
|
||||
startVisionUpdate(element, name, vision, outputMeta)
|
||||
//subscribe to a backwards events propagation for control visions
|
||||
if(vision is ControlVision){
|
||||
vision.controlEventFlow.onEach {
|
||||
vision.eventFlow.onEach {
|
||||
sendEvent(name,it)
|
||||
}.launchIn(context)
|
||||
}
|
||||
|
@ -11,80 +11,35 @@ import java.awt.Desktop
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
|
||||
//
|
||||
///**
|
||||
// * Create a full html string (including the head) for a given [HtmlVisionFragment]
|
||||
// */
|
||||
//@DFExperimental
|
||||
//public fun Context.makeVisionString(
|
||||
// fragment: HtmlVisionFragment,
|
||||
// title: String = "VisionForge page",
|
||||
// headerBuilder: () -> HtmlFragment,
|
||||
//): String = createHTML().apply {
|
||||
// head {
|
||||
// meta {
|
||||
// charset = "utf-8"
|
||||
// fragment(headerBuilder())
|
||||
// }
|
||||
// title(title)
|
||||
// }
|
||||
// body {
|
||||
// embedVisionFragment(visionManager, fragment = fragment)
|
||||
// }
|
||||
//}.finalize()
|
||||
//
|
||||
//
|
||||
///**
|
||||
// * Make a file with the embedded vision data
|
||||
// */
|
||||
//@DFExperimental
|
||||
//public fun Context.makeVisionFile(
|
||||
// fragment: HtmlVisionFragment,
|
||||
// path: Path? = null,
|
||||
// title: String = "VisionForge page",
|
||||
// show: Boolean = true,
|
||||
// headerBuilder: (Path) -> HtmlFragment,
|
||||
//) {
|
||||
// val actualFile = path?.let {
|
||||
// Path.of(System.getProperty("user.home")).resolve(path)
|
||||
// } ?: Files.createTempFile("tempPlot", ".html")
|
||||
// //Files.createDirectories(actualFile.parent)
|
||||
// val htmlString = makeVisionString(fragment, title) { headerBuilder(actualFile) }
|
||||
//
|
||||
// Files.writeString(actualFile, htmlString)
|
||||
// if (show) {
|
||||
// Desktop.getDesktop().browse(actualFile.toFile().toURI())
|
||||
// }
|
||||
//}
|
||||
/**
|
||||
* Render given [VisionPage] to a string using a set of [additionalHeaders] that override current page headers.
|
||||
*/
|
||||
public fun VisionPage.makeString(additionalHeaders: Map<String, HtmlFragment> = emptyMap()): String = createHTML().apply {
|
||||
head {
|
||||
meta {
|
||||
charset = "utf-8"
|
||||
}
|
||||
(pageHeaders + additionalHeaders).values.forEach {
|
||||
appendFragment(it)
|
||||
}
|
||||
}
|
||||
body {
|
||||
visionFragment(Global.visionManager, fragment = content)
|
||||
}
|
||||
}.finalize()
|
||||
|
||||
/**
|
||||
* Export a [VisionPage] to a file
|
||||
*
|
||||
* @param fileHeaders additional file system specific headers.
|
||||
* @param fileHeaders additional file-system-specific headers.
|
||||
*/
|
||||
@DFExperimental
|
||||
public fun VisionPage.makeFile(
|
||||
path: Path?,
|
||||
fileHeaders: ((Path) -> Map<String, HtmlFragment>)? = null,
|
||||
fileHeaders: ((Path) -> Map<String, HtmlFragment>) = { emptyMap() },
|
||||
): Path {
|
||||
val actualFile = path ?: Files.createTempFile("tempPlot", ".html")
|
||||
|
||||
val actualDefaultHeaders = fileHeaders?.invoke(actualFile)
|
||||
val actualHeaders = if (actualDefaultHeaders == null) pageHeaders else actualDefaultHeaders + pageHeaders
|
||||
|
||||
val htmlString = createHTML().apply {
|
||||
head {
|
||||
meta {
|
||||
charset = "utf-8"
|
||||
}
|
||||
actualHeaders.values.forEach {
|
||||
appendFragment(it)
|
||||
}
|
||||
}
|
||||
body {
|
||||
visionFragment(Global.visionManager, fragment = content)
|
||||
}
|
||||
}.finalize()
|
||||
val htmlString = makeString(fileHeaders(actualFile))
|
||||
|
||||
Files.writeString(actualFile, htmlString)
|
||||
return actualFile
|
||||
|
@ -63,7 +63,7 @@ private class GdmlLoader(val settings: GdmlLoaderOptions) {
|
||||
): SolidReference {
|
||||
val templateName = volumesName + volume.name.asName()
|
||||
if (templates[templateName] == null) {
|
||||
templates.setChild(templateName, volume(root, volume))
|
||||
templates.setVision(templateName, volume(root, volume))
|
||||
}
|
||||
val ref = group.ref(templateName, physVolume.name).withPosition(root, physVolume)
|
||||
referenceStore.getOrPut(templateName) { ArrayList() }.add(ref)
|
||||
@ -313,7 +313,7 @@ private class GdmlLoader(val settings: GdmlLoaderOptions) {
|
||||
when (settings.volumeAction(volume)) {
|
||||
GdmlLoaderOptions.Action.ADD -> {
|
||||
val group: SolidGroup = volume(root, volume)
|
||||
this.setChild(physVolume.name, group.withPosition(root, physVolume))
|
||||
this.setVision(physVolume.name, group.withPosition(root, physVolume))
|
||||
}
|
||||
|
||||
GdmlLoaderOptions.Action.PROTOTYPE -> {
|
||||
@ -373,7 +373,7 @@ private class GdmlLoader(val settings: GdmlLoaderOptions) {
|
||||
rootSolid.prototypes {
|
||||
templates.items.forEach { (token, item) ->
|
||||
item.parent = null
|
||||
setChild(token.asName(), item)
|
||||
setVision(token.asName(), item)
|
||||
}
|
||||
}
|
||||
settings.styleCache.forEach {
|
||||
|
@ -4,7 +4,6 @@ import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.gdml.*
|
||||
import space.kscience.visionforge.Vision
|
||||
import space.kscience.visionforge.getChild
|
||||
import space.kscience.visionforge.solid.*
|
||||
import space.kscience.visionforge.visionManager
|
||||
import kotlin.test.Test
|
||||
|
@ -6,7 +6,6 @@ import space.kscience.dataforge.meta.isEmpty
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.visionforge.MutableVisionContainer
|
||||
import space.kscience.visionforge.VisionBuilder
|
||||
import space.kscience.visionforge.setChild
|
||||
import space.kscience.visionforge.static
|
||||
|
||||
public enum class CompositeType {
|
||||
@ -42,7 +41,7 @@ public inline fun MutableVisionContainer<Solid>.composite(
|
||||
|
||||
res.properties[Name.EMPTY] = group.properties.own
|
||||
|
||||
setChild(name, res)
|
||||
setVision(name, res)
|
||||
return res
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,6 @@ import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import space.kscience.visionforge.MutableVisionContainer
|
||||
import space.kscience.visionforge.VisionBuilder
|
||||
import space.kscience.visionforge.setChild
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
|
||||
@ -81,7 +80,7 @@ public inline fun MutableVisionContainer<Solid>.cylinder(
|
||||
r.toFloat(),
|
||||
height.toFloat(),
|
||||
r.toFloat()
|
||||
).apply(block).also { setChild(name, it) }
|
||||
).apply(block).also { setVision(name, it) }
|
||||
|
||||
@VisionBuilder
|
||||
public inline fun MutableVisionContainer<Solid>.cone(
|
||||
@ -98,4 +97,4 @@ public inline fun MutableVisionContainer<Solid>.cone(
|
||||
topRadius = upperRadius.toFloat(),
|
||||
phiStart = startAngle.toFloat(),
|
||||
phi = angle.toFloat()
|
||||
).apply(block).also { setChild(name, it) }
|
||||
).apply(block).also { setVision(name, it) }
|
@ -4,7 +4,6 @@ import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import space.kscience.visionforge.MutableVisionContainer
|
||||
import space.kscience.visionforge.VisionBuilder
|
||||
import space.kscience.visionforge.setChild
|
||||
import kotlin.math.PI
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
@ -137,7 +136,7 @@ public inline fun MutableVisionContainer<Solid>.tube(
|
||||
topInnerRadius = innerRadius.toFloat(),
|
||||
phiStart = startAngle.toFloat(),
|
||||
phi = angle.toFloat()
|
||||
).apply(block).also { setChild(name, it) }
|
||||
).apply(block).also { setVision(name, it) }
|
||||
|
||||
@VisionBuilder
|
||||
public inline fun MutableVisionContainer<Solid>.coneSurface(
|
||||
@ -158,4 +157,4 @@ public inline fun MutableVisionContainer<Solid>.coneSurface(
|
||||
topInnerRadius = topInnerRadius.toFloat(),
|
||||
phiStart = startAngle.toFloat(),
|
||||
phi = angle.toFloat()
|
||||
).apply(block).also { setChild(name, it) }
|
||||
).apply(block).also { setVision(name, it) }
|
@ -3,7 +3,6 @@ package space.kscience.visionforge.solid
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import space.kscience.visionforge.MutableVisionContainer
|
||||
import space.kscience.visionforge.setChild
|
||||
|
||||
/**
|
||||
* A convex hull shape
|
||||
@ -15,7 +14,7 @@ public class Convex(public val points: List<Float32Vector3D>) : SolidBase<Convex
|
||||
public inline fun MutableVisionContainer<Solid>.convex(
|
||||
name: String? = null,
|
||||
action: ConvexBuilder.() -> Unit = {},
|
||||
): Convex = ConvexBuilder().apply(action).build().also { setChild(name, it) }
|
||||
): Convex = ConvexBuilder().apply(action).build().also { setVision(name, it) }
|
||||
|
||||
public class ConvexBuilder {
|
||||
private val points = ArrayList<Float32Vector3D>()
|
||||
|
@ -4,7 +4,6 @@ import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import space.kscience.visionforge.MutableVisionContainer
|
||||
import space.kscience.visionforge.VisionBuilder
|
||||
import space.kscience.visionforge.setChild
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
@ -165,4 +164,4 @@ public inline fun MutableVisionContainer<Solid>.cutTube(
|
||||
phi = angle.toFloat(),
|
||||
nTop = topNormal,
|
||||
nBottom = bottomNormal
|
||||
).apply(block).also { setChild(name, it) }
|
||||
).apply(block).also { setVision(name, it) }
|
||||
|
@ -8,7 +8,6 @@ import space.kscience.kmath.geometry.component1
|
||||
import space.kscience.kmath.geometry.component2
|
||||
import space.kscience.visionforge.MutableVisionContainer
|
||||
import space.kscience.visionforge.VisionBuilder
|
||||
import space.kscience.visionforge.setChild
|
||||
|
||||
|
||||
/**
|
||||
@ -105,4 +104,4 @@ public class Extruded(
|
||||
public fun MutableVisionContainer<Solid>.extruded(
|
||||
name: String? = null,
|
||||
action: Extruded.Builder.() -> Unit = {},
|
||||
): Extruded = Extruded.Builder().apply(action).build().also { setChild(name, it) }
|
||||
): Extruded = Extruded.Builder().apply(action).build().also { setVision(name, it) }
|
@ -4,7 +4,6 @@ import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import space.kscience.visionforge.MutableVisionContainer
|
||||
import space.kscience.visionforge.VisionBuilder
|
||||
import space.kscience.visionforge.setChild
|
||||
|
||||
public interface Hexagon : GeometrySolid {
|
||||
public val node1: Float32Vector3D
|
||||
@ -58,7 +57,7 @@ public inline fun MutableVisionContainer<Solid>.box(
|
||||
zSize: Number,
|
||||
name: String? = null,
|
||||
block: Box.() -> Unit = {},
|
||||
): Box = Box(xSize.toFloat(), ySize.toFloat(), zSize.toFloat()).apply(block).also { setChild(name, it) }
|
||||
): Box = Box(xSize.toFloat(), ySize.toFloat(), zSize.toFloat()).apply(block).also { setVision(name, it) }
|
||||
|
||||
@Serializable
|
||||
@SerialName("solid.hexagon")
|
||||
@ -85,4 +84,4 @@ public inline fun MutableVisionContainer<Solid>.hexagon(
|
||||
node8: Float32Vector3D,
|
||||
name: String? = null,
|
||||
action: Hexagon.() -> Unit = {},
|
||||
): Hexagon = GenericHexagon(node1, node2, node3, node4, node5, node6, node7, node8).apply(action).also { setChild(name, it) }
|
||||
): Hexagon = GenericHexagon(node1, node2, node3, node4, node5, node6, node7, node8).apply(action).also { setVision(name, it) }
|
@ -55,7 +55,7 @@ public class AmbientLightSource : LightSource()
|
||||
public fun MutableVisionContainer<Solid>.ambientLight(
|
||||
name: String? = "@ambientLight",
|
||||
block: AmbientLightSource.() -> Unit = {},
|
||||
): AmbientLightSource = AmbientLightSource().apply(block).also { setChild(name, it) }
|
||||
): AmbientLightSource = AmbientLightSource().apply(block).also { setVision(name, it) }
|
||||
|
||||
@Serializable
|
||||
@SerialName("solid.light.point")
|
||||
@ -71,5 +71,5 @@ public fun MutableVisionContainer<Solid>.pointLight(
|
||||
block: PointLightSource.() -> Unit = {},
|
||||
): PointLightSource = PointLightSource().apply(block).also {
|
||||
it.position = Float32Vector3D(x, y, z)
|
||||
setChild(name, it)
|
||||
setVision(name, it)
|
||||
}
|
@ -4,7 +4,6 @@ import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import space.kscience.visionforge.MutableVisionContainer
|
||||
import space.kscience.visionforge.VisionBuilder
|
||||
import space.kscience.visionforge.setChild
|
||||
|
||||
/**
|
||||
* Utility solids
|
||||
@ -29,5 +28,5 @@ public fun MutableVisionContainer<Solid>.axes(
|
||||
name: String = "@axes",
|
||||
block: AxesSolid.() -> Unit = {},
|
||||
): AxesSolid = AxesSolid(size.toDouble()).apply(block).also {
|
||||
setChild(name, it)
|
||||
setVision(name, it)
|
||||
}
|
@ -6,7 +6,6 @@ import space.kscience.dataforge.meta.number
|
||||
import space.kscience.visionforge.MutableVisionContainer
|
||||
import space.kscience.visionforge.VisionBuilder
|
||||
import space.kscience.visionforge.root
|
||||
import space.kscience.visionforge.setChild
|
||||
|
||||
@Serializable
|
||||
@SerialName("solid.line")
|
||||
@ -28,4 +27,4 @@ public fun MutableVisionContainer<Solid>.polyline(
|
||||
vararg points: Float32Vector3D,
|
||||
name: String? = null,
|
||||
action: PolyLine.() -> Unit = {},
|
||||
): PolyLine = PolyLine(points.toList()).apply(action).also { setChild(name, it) }
|
||||
): PolyLine = PolyLine(points.toList()).apply(action).also { setVision(name, it) }
|
@ -6,7 +6,6 @@ import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.NameToken
|
||||
import space.kscience.dataforge.names.parseAsName
|
||||
import space.kscience.visionforge.AbstractVisionGroup
|
||||
import space.kscience.visionforge.MutableVisionContainer
|
||||
import space.kscience.visionforge.MutableVisionGroup
|
||||
import space.kscience.visionforge.VisionBuilder
|
||||
@ -34,8 +33,7 @@ public interface PrototypeHolder {
|
||||
*/
|
||||
@Serializable
|
||||
@SerialName("group.solid")
|
||||
public class SolidGroup : AbstractVisionGroup(), Solid, PrototypeHolder, MutableVisionGroup,
|
||||
MutableVisionContainer<Solid> {
|
||||
public class SolidGroup : Solid, PrototypeHolder, MutableVisionGroup<Solid>{
|
||||
|
||||
public val items: Map<NameToken, Solid>
|
||||
get() = children.keys.mapNotNull {
|
||||
@ -78,8 +76,8 @@ public class SolidGroup : AbstractVisionGroup(), Solid, PrototypeHolder, Mutable
|
||||
// super.update(change)
|
||||
// }
|
||||
|
||||
override fun setChild(name: Name?, child: Solid?) {
|
||||
children.setChild(name, child)
|
||||
override fun setVision(name: Name?, vision: Solid?) {
|
||||
children.setChild(name, vision)
|
||||
}
|
||||
|
||||
public companion object {
|
||||
@ -93,7 +91,7 @@ public operator fun SolidGroup.get(name:String): Solid? = get(name.parseAsName()
|
||||
public inline fun MutableVisionContainer<Solid>.solidGroup(
|
||||
name: Name? = null,
|
||||
builder: SolidGroup.() -> Unit = {},
|
||||
): SolidGroup = SolidGroup().also { setChild(name, it) }.apply(builder)
|
||||
): SolidGroup = SolidGroup().also { setVision(name, it) }.apply(builder)
|
||||
//root first, update later
|
||||
|
||||
/**
|
||||
|
@ -4,7 +4,6 @@ import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import space.kscience.visionforge.MutableVisionContainer
|
||||
import space.kscience.visionforge.VisionBuilder
|
||||
import space.kscience.visionforge.setChild
|
||||
|
||||
@Serializable
|
||||
@SerialName("solid.label")
|
||||
@ -21,4 +20,4 @@ public fun MutableVisionContainer<Solid>.label(
|
||||
fontFamily: String = "Arial",
|
||||
name: String? = null,
|
||||
action: SolidLabel.() -> Unit = {},
|
||||
): SolidLabel = SolidLabel(text, fontSize.toDouble(), fontFamily).apply(action).also { setChild(name, it) }
|
||||
): SolidLabel = SolidLabel(text, fontSize.toDouble(), fontFamily).apply(action).also { setVision(name, it) }
|
@ -212,7 +212,7 @@ internal class SolidReferenceChild(
|
||||
public fun MutableVisionContainer<Solid>.ref(
|
||||
templateName: Name,
|
||||
name: Name? = null,
|
||||
): SolidReference = SolidReference(templateName).also { setChild(name, it) }
|
||||
): SolidReference = SolidReference(templateName).also { setVision(name, it) }
|
||||
|
||||
public fun MutableVisionContainer<Solid>.ref(
|
||||
templateName: Name,
|
||||
@ -231,7 +231,7 @@ public fun SolidGroup.newRef(
|
||||
val existing = prototypeHolder.getPrototype(prototypeName)
|
||||
if (existing == null) {
|
||||
prototypeHolder.prototypes {
|
||||
setChild(prototypeName, obj)
|
||||
setVision(prototypeName, obj)
|
||||
}
|
||||
} else if (existing != obj) {
|
||||
error("Can't add different prototype on top of existing one")
|
||||
|
@ -22,8 +22,8 @@ public class Solids(meta: Meta) : VisionPlugin(meta), MutableVisionContainer<Sol
|
||||
|
||||
override val visionSerializersModule: SerializersModule get() = serializersModuleForSolids
|
||||
|
||||
override fun setChild(name: Name?, child: Solid?) {
|
||||
child?.setAsRoot(visionManager)
|
||||
override fun setVision(name: Name?, vision: Solid?) {
|
||||
vision?.setAsRoot(visionManager)
|
||||
}
|
||||
|
||||
public companion object : PluginFactory<Solids> {
|
||||
|
@ -4,7 +4,6 @@ import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import space.kscience.visionforge.MutableVisionContainer
|
||||
import space.kscience.visionforge.VisionBuilder
|
||||
import space.kscience.visionforge.setChild
|
||||
import kotlin.math.PI
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
@ -58,4 +57,4 @@ public inline fun MutableVisionContainer<Solid>.sphere(
|
||||
action: Sphere.() -> Unit = {},
|
||||
): Sphere = Sphere(
|
||||
radius.toFloat(),
|
||||
).apply(action).also { setChild(name, it) }
|
||||
).apply(action).also { setVision(name, it) }
|
@ -4,7 +4,6 @@ import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import space.kscience.visionforge.MutableVisionContainer
|
||||
import space.kscience.visionforge.VisionBuilder
|
||||
import space.kscience.visionforge.setChild
|
||||
import kotlin.math.PI
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
@ -85,4 +84,4 @@ public inline fun MutableVisionContainer<Solid>.sphereLayer(
|
||||
phi.toFloat(),
|
||||
thetaStart.toFloat(),
|
||||
theta.toFloat()
|
||||
).apply(action).also { setChild(name, it) }
|
||||
).apply(action).also { setVision(name, it) }
|
@ -4,7 +4,6 @@ import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import space.kscience.visionforge.MutableVisionContainer
|
||||
import space.kscience.visionforge.VisionBuilder
|
||||
import space.kscience.visionforge.setChild
|
||||
|
||||
|
||||
public sealed class StlSolid: SolidBase<StlSolid>()
|
||||
@ -22,4 +21,4 @@ public inline fun MutableVisionContainer<Solid>.stl(
|
||||
url: String,
|
||||
name: String? = null,
|
||||
action: StlSolid.() -> Unit = {},
|
||||
): StlSolid = StlUrlSolid(url).apply(action).also { setChild(name, it) }
|
||||
): StlSolid = StlUrlSolid(url).apply(action).also { setVision(name, it) }
|
@ -9,7 +9,6 @@ import space.kscience.kmath.geometry.component2
|
||||
import space.kscience.kmath.structures.Float32
|
||||
import space.kscience.visionforge.MutableVisionContainer
|
||||
import space.kscience.visionforge.VisionBuilder
|
||||
import space.kscience.visionforge.setChild
|
||||
|
||||
|
||||
private inline fun <T> Iterable<T>.sumOf(selector: (T) -> Float32): Float32 {
|
||||
@ -170,4 +169,4 @@ public class Surface(
|
||||
public fun MutableVisionContainer<Solid>.surface(
|
||||
name: String? = null,
|
||||
action: Surface.Builder.() -> Unit = {},
|
||||
): Surface = Surface.Builder().apply(action).build().also { setChild(name, it) }
|
||||
): Surface = Surface.Builder().apply(action).build().also { setVision(name, it) }
|
||||
|
@ -27,7 +27,7 @@ internal object UnRef : VisualTreeTransform<SolidGroup>() {
|
||||
|
||||
private fun SolidGroup.unref(name: Name) {
|
||||
prototypes{
|
||||
setChild(name, null)
|
||||
setVision(name, null)
|
||||
}
|
||||
items.filter { (it.value as? SolidReference)?.prototypeName == name }.forEach { (key, value) ->
|
||||
val reference = value as SolidReference
|
||||
|
@ -1,7 +1,6 @@
|
||||
package space.kscience.visionforge.solid
|
||||
|
||||
import space.kscience.visionforge.Colors
|
||||
import space.kscience.visionforge.getChild
|
||||
import kotlin.math.PI
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
@ -2,7 +2,6 @@ package space.kscience.visionforge.solid
|
||||
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.visionforge.Colors
|
||||
import space.kscience.visionforge.getChild
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
|
@ -3,7 +3,6 @@ package space.kscience.visionforge.solid
|
||||
import space.kscience.dataforge.context.Global
|
||||
import space.kscience.dataforge.context.request
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.visionforge.getChild
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
|
@ -3,7 +3,6 @@ package space.kscience.visionforge.solid
|
||||
import kotlinx.serialization.json.encodeToJsonElement
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.dataforge.names.get
|
||||
import space.kscience.visionforge.getChild
|
||||
import space.kscience.visionforge.style
|
||||
import space.kscience.visionforge.useStyle
|
||||
import kotlin.test.Test
|
||||
|
@ -6,7 +6,6 @@ import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.asValue
|
||||
import space.kscience.dataforge.names.asName
|
||||
import space.kscience.visionforge.VisionChange
|
||||
import space.kscience.visionforge.getChild
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
|
@ -29,4 +29,4 @@ public fun makeThreeJsFile(
|
||||
)
|
||||
}
|
||||
if (show) Desktop.getDesktop().browse(actualPath.toFile().toURI())
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user