[WIP] refactor inner logic

This commit is contained in:
Alexander Nozik 2024-08-21 15:39:08 +03:00
parent 50bff2b729
commit 7d3078879d
64 changed files with 686 additions and 752 deletions

3
.gitignore vendored
View File

@ -3,10 +3,9 @@
*.iws
out/
.gradle
.kotlin
.kotlin/
build/
data/
.kotlin/
jcef-bundle/

View File

@ -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()

View File

@ -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)
}
}

View File

@ -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

View File

@ -61,7 +61,7 @@ class Model(val manager: VisionManager) {
}
}
setChild("tracks".asName(), tracks)
setVision("tracks".asName(), tracks)
}
private fun highlight(pixel: String) {

View File

@ -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() {

View File

@ -16,6 +16,8 @@ kotlin {
sourceSets {
jvmMain {
dependencies {
implementation(projects.plotly.plotlyktCore)
implementation(compose.runtime)
implementation(compose.foundation)
implementation(compose.material)

View File

@ -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 ->

View 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")
}
}

View 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)
}

View 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)

View File

@ -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{
}
}
}

View File

@ -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)

View File

@ -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)
}

View File

@ -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? {

View File

@ -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")
}
```

View File

@ -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
}

View File

@ -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;
}

View File

@ -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"

View File

@ -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)
}
}

View File

@ -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 {

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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())

View File

@ -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

View File

@ -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)
}
}
}

View File

@ -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
*/

View File

@ -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.

View File

@ -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()
}

View File

@ -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]
*/

View File

@ -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)

View File

@ -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(

View File

@ -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)
}
/**

View File

@ -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)
}

View File

@ -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

View File

@ -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 {

View File

@ -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

View File

@ -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
}

View File

@ -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) }

View File

@ -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) }

View File

@ -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>()

View File

@ -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) }

View File

@ -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) }

View File

@ -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) }

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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) }

View File

@ -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
/**

View File

@ -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) }

View File

@ -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")

View File

@ -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> {

View File

@ -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) }

View File

@ -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) }

View File

@ -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) }

View File

@ -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) }

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -29,4 +29,4 @@ public fun makeThreeJsFile(
)
}
if (show) Desktop.getDesktop().browse(actualPath.toFile().toURI())
}
}