Moving to compose

This commit is contained in:
Alexander Nozik 2023-12-18 09:41:05 +03:00
parent 0c9d849e97
commit 4f6f4b9268
59 changed files with 765 additions and 1050 deletions

View File

@ -5,6 +5,7 @@
- Context receivers flag - Context receivers flag
- MeshLine for thick lines - MeshLine for thick lines
- Custom client-side events and thier processing in VisionServer - Custom client-side events and thier processing in VisionServer
- Control/input visions
### Changed ### Changed
- Color accessor property is now `colorProperty`. Color uses non-nullable `invoke` instead of `set`. - Color accessor property is now `colorProperty`. Color uses non-nullable `invoke` instead of `set`.

View File

@ -1,4 +1,3 @@
import org.jetbrains.kotlin.gradle.dsl.KotlinJsCompile
import space.kscience.gradle.useApache2Licence import space.kscience.gradle.useApache2Licence
import space.kscience.gradle.useSPCTeam import space.kscience.gradle.useSPCTeam
@ -11,7 +10,7 @@ val dataforgeVersion by extra("0.7.1")
allprojects { allprojects {
group = "space.kscience" group = "space.kscience"
version = "0.3.0-RC" version = "0.4.0-dev-1"
} }
subprojects { subprojects {
@ -31,11 +30,11 @@ subprojects {
} }
} }
tasks.withType<KotlinJsCompile>{ // tasks.withType<KotlinJsCompile>{
kotlinOptions{ // kotlinOptions{
useEsClasses = true // useEsClasses = true
} // }
} // }
} }
ksciencePublish { ksciencePublish {

View File

@ -7,14 +7,8 @@ group = "demo"
kscience { kscience {
jvm() jvm()
js { js {
useCommonJs()
browser { browser {
binaries.executable() binaries.executable()
commonWebpackConfig {
cssSupport {
enabled.set(false)
}
}
} }
} }
dependencies { dependencies {
@ -26,7 +20,6 @@ kscience {
implementation(spclibs.logback.classic) implementation(spclibs.logback.classic)
} }
jsMain { jsMain {
implementation(projects.ui.ring)
implementation(projects.visionforgeThreejs) implementation(projects.visionforgeThreejs)
implementation(npm("react-file-drop", "3.0.6")) implementation(npm("react-file-drop", "3.0.6"))
} }

View File

@ -1,46 +1,33 @@
package space.kscience.visionforge.gdml.demo package space.kscience.visionforge.gdml.demo
import androidx.compose.runtime.*
import kotlinx.browser.window import kotlinx.browser.window
import kotlinx.coroutines.CompletableDeferred import org.jetbrains.compose.web.css.*
import kotlinx.coroutines.Deferred import org.jetbrains.compose.web.dom.Div
import kotlinx.css.* import org.jetbrains.compose.web.dom.H2
import org.jetbrains.compose.web.dom.Text
import org.w3c.files.File import org.w3c.files.File
import org.w3c.files.FileReader import org.w3c.files.FileReader
import org.w3c.files.get import org.w3c.files.get
import react.Props
import react.dom.h2
import react.fc
import react.useState
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.gdml.Gdml import space.kscience.gdml.Gdml
import space.kscience.gdml.decodeFromString import space.kscience.gdml.decodeFromString
import space.kscience.visionforge.Colors import space.kscience.visionforge.Colors
import space.kscience.visionforge.gdml.markLayers import space.kscience.visionforge.gdml.markLayers
import space.kscience.visionforge.gdml.toVision import space.kscience.visionforge.gdml.toVision
import space.kscience.visionforge.ring.ThreeCanvasWithControls
import space.kscience.visionforge.ring.tab
import space.kscience.visionforge.setAsRoot import space.kscience.visionforge.setAsRoot
import space.kscience.visionforge.solid.Solid import space.kscience.visionforge.solid.Solid
import space.kscience.visionforge.solid.Solids import space.kscience.visionforge.solid.Solids
import space.kscience.visionforge.solid.ambientLight import space.kscience.visionforge.solid.ambientLight
import space.kscience.visionforge.solid.invoke import space.kscience.visionforge.solid.invoke
import styled.css import space.kscience.visionforge.solid.three.compose.ThreeView
import styled.styledDiv
external interface GDMLAppProps : Props {
var solids: Solids
var vision: Solid?
var selected: Name?
}
@JsExport @Composable
val GDMLApp = fc<GDMLAppProps>("GDMLApp") { props -> fun GDMLApp(solids: Solids, initialVision: Solid?, selected: Name? = null) {
var deferredVision: Deferred<Solid?> by useState { var vision: Solid? by remember { mutableStateOf(initialVision) }
CompletableDeferred(props.vision)
}
fun readFileAsync(file: File): Deferred<Solid?> { fun readFileAsync(file: File) {
val deferred = CompletableDeferred<Solid?>()
FileReader().apply { FileReader().apply {
onload = { onload = {
val data = result as String val data = result as String
@ -49,7 +36,7 @@ val GDMLApp = fc<GDMLAppProps>("GDMLApp") { props ->
name.endsWith(".gdml") || name.endsWith(".xml") -> { name.endsWith(".gdml") || name.endsWith(".xml") -> {
val gdml = Gdml.decodeFromString(data) val gdml = Gdml.decodeFromString(data)
gdml.toVision().apply { gdml.toVision().apply {
setAsRoot(props.solids.visionManager) setAsRoot(solids.visionManager)
console.info("Marking layers for file $name") console.info("Marking layers for file $name")
markLayers() markLayers()
ambientLight { ambientLight {
@ -57,44 +44,39 @@ val GDMLApp = fc<GDMLAppProps>("GDMLApp") { props ->
} }
} }
} }
name.endsWith(".json") -> props.solids.visionManager.decodeFromString(data)
name.endsWith(".json") -> solids.visionManager.decodeFromString(data)
else -> { else -> {
window.alert("File extension is not recognized: $name") window.alert("File extension is not recognized: $name")
error("File extension is not recognized: $name") error("File extension is not recognized: $name")
} }
} }
deferred.complete(parsedVision as? Solid ?: error("Parsed vision is not a solid")) vision = parsedVision as? Solid ?: error("Parsed vision is not a solid")
Unit
} }
readAsText(file) readAsText(file)
} }
return deferred
} }
styledDiv { Div({
css { style {
height = 100.vh - 12.pt height(100.vh - 12.pt)
width = 100.vw width(100.vw)
} }
child(ThreeCanvasWithControls) { }) {
attrs { ThreeView(solids, vision, selected) {
this.solids = props.solids Tab("Load") {
this.builderOfSolid = deferredVision H2 {
this.selected = props.selected Text("Drag and drop .gdml or .json VisionForge files here")
tab("Load") {
h2 {
+"Drag and drop .gdml or .json VisionForge files here"
} }
fileDrop("(drag file here)") { files -> fileDrop("(drag file here)") { files ->
val file = files?.get(0) val file = files?.get(0)
if (file != null) { if (file != null) {
deferredVision = readFileAsync(file) readFileAsync(file)
} }
} }
} }
} }
}
} }
} }

View File

@ -1,63 +1,58 @@
package space.kscience.visionforge.gdml.demo package space.kscience.visionforge.gdml.demo
import kotlinx.css.* import org.jetbrains.compose.web.css.*
import org.jetbrains.compose.web.dom.Style
import org.jetbrains.compose.web.renderComposable
import org.w3c.dom.Document import org.w3c.dom.Document
import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Context
import space.kscience.dataforge.context.request import space.kscience.dataforge.context.request
import space.kscience.gdml.GdmlShowCase import space.kscience.gdml.GdmlShowCase
import space.kscience.visionforge.Application import space.kscience.visionforge.Application
import space.kscience.visionforge.Colors import space.kscience.visionforge.Colors
import space.kscience.visionforge.compose.TreeStyles
import space.kscience.visionforge.gdml.toVision import space.kscience.visionforge.gdml.toVision
import space.kscience.visionforge.react.createRoot
import space.kscience.visionforge.react.render
import space.kscience.visionforge.solid.Solids import space.kscience.visionforge.solid.Solids
import space.kscience.visionforge.solid.ambientLight import space.kscience.visionforge.solid.ambientLight
import space.kscience.visionforge.solid.invoke import space.kscience.visionforge.solid.invoke
import space.kscience.visionforge.solid.three.ThreePlugin import space.kscience.visionforge.solid.three.ThreePlugin
import space.kscience.visionforge.startApplication import space.kscience.visionforge.startApplication
import styled.injectGlobal
private class GDMLDemoApp : Application { private class GDMLDemoApp : Application {
override fun start(document: Document, state: Map<String, Any>) { override fun start(document: Document, state: Map<String, Any>) {
val context = Context("gdml-demo"){ val context = Context("gdml-demo") {
plugin(ThreePlugin) plugin(ThreePlugin)
} }
injectGlobal {
html{
height = 100.pct
}
body{
height = 100.pct
display = Display.flex
alignItems = Align.stretch
}
"#application"{
width = 100.pct
display = Display.flex
alignItems = Align.stretch
}
}
val element = document.getElementById("application") ?: error("Element with id 'application' not found on page") val element = document.getElementById("application") ?: error("Element with id 'application' not found on page")
createRoot(element).render {
child(GDMLApp) {
val vision = GdmlShowCase.cubes().toVision().apply { val vision = GdmlShowCase.cubes().toVision().apply {
ambientLight { ambientLight {
color(Colors.white) color(Colors.white)
} }
} }
//println(context.plugins.fetch(VisionManager).encodeToString(vision))
attrs { renderComposable(element) {
this.solids = context.request(Solids) Style(TreeStyles)
this.vision = vision Style {
"html" {
height(100.percent)
}
"body" {
height(100.percent)
display(DisplayStyle.Flex)
alignItems(AlignItems.Stretch)
}
"#application" {
width(100.percent)
display(DisplayStyle.Flex)
alignItems(AlignItems.Stretch)
} }
} }
GDMLApp(context.request(Solids), vision)
} }
} }
} }

View File

@ -9,14 +9,8 @@ kscience {
kotlin { kotlin {
explicitApi = null explicitApi = null
js { js {
useCommonJs()
browser { browser {
binaries.executable() binaries.executable()
commonWebpackConfig {
cssSupport {
enabled.set(false)
}
}
} }
} }
} }
@ -29,7 +23,4 @@ kscience {
implementation(projects.visionforge.visionforgeMarkdown) implementation(projects.visionforge.visionforgeMarkdown)
implementation(projects.visionforge.visionforgeThreejs) implementation(projects.visionforge.visionforgeThreejs)
} }
jsMain {
implementation(projects.ui.ring)
}
} }

View File

@ -1,7 +1,7 @@
import kotlinx.css.* import org.jetbrains.compose.web.css.*
import org.jetbrains.compose.web.dom.Div
import org.jetbrains.compose.web.renderComposable
import org.w3c.dom.Document import org.w3c.dom.Document
import ringui.SmartTabs
import ringui.Tab
import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Context
import space.kscience.dataforge.context.request import space.kscience.dataforge.context.request
import space.kscience.plotly.models.Trace import space.kscience.plotly.models.Trace
@ -9,16 +9,12 @@ import space.kscience.plotly.scatter
import space.kscience.visionforge.Application import space.kscience.visionforge.Application
import space.kscience.visionforge.Colors import space.kscience.visionforge.Colors
import space.kscience.visionforge.JsVisionClient import space.kscience.visionforge.JsVisionClient
import space.kscience.visionforge.compose.Tabs
import space.kscience.visionforge.plotly.PlotlyPlugin import space.kscience.visionforge.plotly.PlotlyPlugin
import space.kscience.visionforge.react.createRoot
import space.kscience.visionforge.react.render
import space.kscience.visionforge.ring.ThreeCanvasWithControls
import space.kscience.visionforge.ring.ThreeWithControlsPlugin
import space.kscience.visionforge.ring.solid
import space.kscience.visionforge.solid.* import space.kscience.visionforge.solid.*
import space.kscience.visionforge.solid.three.ThreePlugin
import space.kscience.visionforge.solid.three.compose.ThreeView
import space.kscience.visionforge.startApplication import space.kscience.visionforge.startApplication
import styled.css
import styled.styledDiv
import kotlin.random.Random import kotlin.random.Random
fun Trace.appendXYLatest(x: Number, y: Number, history: Int = 400, xErr: Number? = null, yErr: Number? = null) { fun Trace.appendXYLatest(x: Number, y: Number, history: Int = 400, xErr: Number? = null, yErr: Number? = null) {
@ -33,28 +29,28 @@ private class JsPlaygroundApp : Application {
override fun start(document: Document, state: Map<String, Any>) { override fun start(document: Document, state: Map<String, Any>) {
val playgroundContext = Context { val playgroundContext = Context {
plugin(ThreeWithControlsPlugin) plugin(ThreePlugin)
plugin(JsVisionClient)
plugin(PlotlyPlugin) plugin(PlotlyPlugin)
} }
val solids = playgroundContext.request(Solids)
val client = playgroundContext.request(JsVisionClient)
val element = document.getElementById("playground") ?: error("Element with id 'playground' not found on page") val element = document.getElementById("playground") ?: error("Element with id 'playground' not found on page")
createRoot(element).render { renderComposable(element) {
styledDiv { Div({
css { style {
padding = Padding(0.pt) padding(0.pt)
margin = Margin(0.pt) margin(0.pt)
height = 100.vh height(100.vh)
width = 100.vw width(100.vw)
} }
SmartTabs("gravity") { }) {
Tabs {
active = "gravity"
Tab("gravity") { Tab("gravity") {
GravityDemo { GravityDemo(solids, client)
attrs {
this.solids = playgroundContext.request(Solids)
}
}
} }
// Tab("D0") { // Tab("D0") {
@ -66,38 +62,31 @@ private class JsPlaygroundApp : Application {
// } // }
// } // }
Tab("spheres") { Tab("spheres") {
styledDiv { Div({
css { style {
height = 100.vh - 50.pt height(100.vh - 50.pt)
} }
child(ThreeCanvasWithControls) { }) {
val random = Random(112233) ThreeView(solids, SolidGroup {
attrs {
solids = playgroundContext.request(Solids)
solid {
ambientLight { ambientLight {
color(Colors.white) color(Colors.white)
} }
repeat(100) { repeat(100) {
sphere(5, name = "sphere[$it]") { sphere(5, name = "sphere[$it]") {
x = random.nextDouble(-300.0, 300.0) x = Random.nextDouble(-300.0, 300.0)
y = random.nextDouble(-300.0, 300.0) y = Random.nextDouble(-300.0, 300.0)
z = random.nextDouble(-300.0, 300.0) z = Random.nextDouble(-300.0, 300.0)
material { material {
color(random.nextInt()) color(Random.nextInt())
} }
detail = 16 detail = 16
} }
} }
} })
}
}
} }
} }
Tab("plotly") { Tab("plotly") {
Plotly { Plot(client) {
attrs {
plot = space.kscience.plotly.Plotly.plot {
scatter { scatter {
x(1, 2, 3) x(1, 2, 3)
y(5, 8, 7) y(5, 8, 7)
@ -108,8 +97,7 @@ private class JsPlaygroundApp : Application {
} }
} }
} }
}
}
} }
public fun main() { public fun main() {

View File

@ -1,47 +1,75 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch import org.jetbrains.compose.web.css.*
import kotlinx.css.* import org.jetbrains.compose.web.dom.AttrBuilderContext
import react.Props import org.jetbrains.compose.web.dom.Div
import react.fc import org.w3c.dom.HTMLDivElement
import space.kscience.dataforge.meta.Meta
import space.kscience.plotly.Plot
import space.kscience.plotly.layout import space.kscience.plotly.layout
import space.kscience.plotly.models.Trace import space.kscience.plotly.models.Trace
import space.kscience.visionforge.Colors import space.kscience.visionforge.Colors
import space.kscience.visionforge.VisionClient
import space.kscience.visionforge.compose.FlexRow
import space.kscience.visionforge.compose.Vision
import space.kscience.visionforge.compose.zIndex
import space.kscience.visionforge.markup.VisionOfMarkup import space.kscience.visionforge.markup.VisionOfMarkup
import space.kscience.visionforge.react.flexRow import space.kscience.visionforge.plotly.asVision
import space.kscience.visionforge.ring.ThreeCanvasWithControls
import space.kscience.visionforge.ring.solid
import space.kscience.visionforge.solid.* import space.kscience.visionforge.solid.*
import styled.css import space.kscience.visionforge.solid.three.compose.ThreeView
import styled.styledDiv
import kotlin.math.sqrt import kotlin.math.sqrt
external interface DemoProps : Props { @Composable
var solids: Solids fun Plot(
} client: VisionClient,
meta: Meta = Meta.EMPTY,
attrs: AttrBuilderContext<HTMLDivElement>? = null,
block: Plot.() -> Unit,
) = Vision(
client = client,
attrs = attrs,
meta = meta,
vision = Plot().apply(block).asVision()
)
val GravityDemo = fc<DemoProps> { props -> @Composable
val velocityTrace = Trace { fun Markup(
client: VisionClient,
markup: VisionOfMarkup,
meta: Meta = Meta.EMPTY,
attrs: AttrBuilderContext<HTMLDivElement>? = null,
) = Vision(
client = client,
attrs = attrs,
meta = meta,
vision = markup
)
private val h = 100.0
@Composable
fun GravityDemo(solids: Solids, client: VisionClient) {
val velocityTrace = remember {
Trace {
name = "velocity" name = "velocity"
} }
val energyTrace = Trace { }
val energyTrace = remember {
Trace {
name = "energy" name = "energy"
} }
val markup = VisionOfMarkup() }
styledDiv { val markup = remember { VisionOfMarkup() }
css {
height = 100.vh - 50.pt val solid = remember {
} SolidGroup {
styledDiv { pointLight(200, 200, 200, name = "light") {
css {
height = 50.vh
}
child(ThreeCanvasWithControls) {
attrs {
solids = props.solids
solid {
pointLight(200, 200, 200, name = "light"){
color(Colors.white) color(Colors.white)
} }
ambientLight() ambientLight()
@ -49,9 +77,18 @@ val GravityDemo = fc<DemoProps> { props ->
sphere(5.0, "ball") { sphere(5.0, "ball") {
detail = 16 detail = 16
color("red") color("red")
val h = 100.0
y = h y = h
solids.context.launch {
box(200, 5, 200, name = "floor") {
y = -2.5
}
}
}
}
LaunchedEffect(solid) {
val ball = solid["ball"]!!
val g = 10.0 val g = 10.0
val dt = 0.1 val dt = 0.1
var time = 0.0 var time = 0.0
@ -60,13 +97,13 @@ val GravityDemo = fc<DemoProps> { props ->
delay(20) delay(20)
time += dt time += dt
velocity -= g * dt velocity -= g * dt
val energy = g * y.toDouble() + velocity * velocity / 2 val energy = g * ball.y.toDouble() + velocity * velocity / 2
y = y.toDouble() + velocity * dt ball.y = ball.y.toDouble() + velocity * dt
velocityTrace.appendXYLatest(time, y) velocityTrace.appendXYLatest(time, ball.y)
energyTrace.appendXYLatest(time, energy) energyTrace.appendXYLatest(time, energy)
if (y.toDouble() <= 2.5) { if (ball.y.toDouble() <= 2.5) {
//conservation of energy //conservation of energy
velocity = sqrt(2 * g * h) velocity = sqrt(2 * g * h)
} }
@ -80,32 +117,43 @@ val GravityDemo = fc<DemoProps> { props ->
""".trimIndent() """.trimIndent()
} }
} }
}
box(200, 5, 200, name = "floor") { Div({
y = -2.5 style {
height(100.vh - 50.pt)
} }
}) {
Div({
style {
height(50.vh)
} }
}) {
ThreeView(solids, solid)
} }
FlexRow({
style {
alignContent(AlignContent.Stretch)
alignItems(AlignItems.Stretch)
height(50.vh - 50.pt)
} }
} }) {
flexRow { Plot(client) {
css {
alignContent = Align.stretch
alignItems = Align.stretch
height = 50.vh - 50.pt
}
plotly {
traces(velocityTrace, energyTrace) traces(velocityTrace, energyTrace)
layout { layout {
xaxis.title = "time" xaxis.title = "time"
} }
} }
Markup { Markup(client, markup, attrs = {
attrs { style {
this.markup = markup width(100.percent)
} height(100.percent)
border(2.pt, LineStyle.Solid, Color.blue)
paddingLeft(8.pt)
backgroundColor(Color.white)
flex(1)
zIndex(10000)
} }
})
} }
} }
} }

View File

@ -1,55 +0,0 @@
import kotlinx.css.*
import kotlinx.dom.clear
import kotlinx.html.dom.append
import org.intellij.markdown.flavours.commonmark.CommonMarkFlavourDescriptor
import org.intellij.markdown.flavours.gfm.GFMFlavourDescriptor
import org.w3c.dom.Element
import org.w3c.dom.HTMLElement
import react.Props
import react.fc
import react.useEffect
import react.useRef
import space.kscience.visionforge.markup.VisionOfMarkup
import space.kscience.visionforge.markup.markdown
import space.kscience.visionforge.useProperty
import styled.css
import styled.styledDiv
external interface MarkupProps : Props {
var markup: VisionOfMarkup?
}
val Markup = fc<MarkupProps>("Markup") { props ->
val elementRef = useRef<Element>(null)
useEffect(props.markup, elementRef) {
val element = elementRef.current as? HTMLElement ?: error("Markup element not found")
props.markup?.let { vision ->
val flavour = when (vision.format) {
VisionOfMarkup.COMMONMARK_FORMAT -> CommonMarkFlavourDescriptor()
VisionOfMarkup.GFM_FORMAT -> GFMFlavourDescriptor()
//TODO add new formats via plugins
else -> error("Format ${vision.format} not recognized")
}
vision.useProperty(VisionOfMarkup::content) { content: String? ->
element.clear()
element.append {
markdown(flavour) { content ?: "" }
}
}
}
}
styledDiv {
css {
width = 100.pct
height = 100.pct
border= Border(2.pt, BorderStyle.solid, Color.blue)
padding = Padding(left = 8.pt)
backgroundColor = Color.white
flex = Flex(1.0)
zIndex = 10000
}
ref = elementRef
}
}

View File

@ -1,43 +0,0 @@
import kotlinx.css.*
import org.w3c.dom.Element
import org.w3c.dom.HTMLElement
import react.*
import space.kscience.plotly.Plot
import space.kscience.plotly.PlotlyConfig
import space.kscience.plotly.plot
import styled.css
import styled.styledDiv
external interface PlotlyProps : Props {
var plot: Plot?
}
val Plotly = fc<PlotlyProps>("Plotly") { props ->
val elementRef = useRef<Element>(null)
useEffect(props.plot, elementRef) {
val element = elementRef.current as? HTMLElement ?: error("Plotly element not found")
props.plot?.let {
element.plot(PlotlyConfig {
responsive = true
}, it)
}
}
styledDiv {
css {
width = 100.pct
height = 100.pct
border = Border(2.pt, BorderStyle.solid, Color.blue)
flex = Flex(1.0)
}
ref = elementRef
}
}
fun RBuilder.plotly(plotbuilder: Plot.() -> Unit) = Plotly {
attrs {
this.plot = Plot().apply(plotbuilder)
}
}

View File

@ -15,13 +15,7 @@ kscience {
"muon-monitor.js", "muon-monitor.js",
jvmConfig = { withJava() }, jvmConfig = { withJava() },
jsConfig = { useCommonJs() } jsConfig = { useCommonJs() }
) { )
commonWebpackConfig {
cssSupport {
enabled.set(false)
}
}
}
commonMain { commonMain {
implementation(projects.visionforgeSolid) implementation(projects.visionforgeSolid)
@ -34,7 +28,6 @@ kscience {
implementation("ch.qos.logback:logback-classic:1.2.11") implementation("ch.qos.logback:logback-classic:1.2.11")
} }
jsMain { jsMain {
implementation(projects.ui.ring)
implementation(projects.visionforgeThreejs) implementation(projects.visionforgeThreejs)
//implementation(devNpm("webpack-bundle-analyzer", "4.4.0")) //implementation(devNpm("webpack-bundle-analyzer", "4.4.0"))
} }

View File

@ -1,87 +1,67 @@
package ru.mipt.npm.muon.monitor package ru.mipt.npm.muon.monitor
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.remember
import app.softwork.bootstrapcompose.Button
import kotlinx.browser.window import kotlinx.browser.window
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.await import kotlinx.coroutines.await
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.css.*
import kotlinx.html.js.onClickFunction
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import org.jetbrains.compose.web.css.*
import org.jetbrains.compose.web.dom.Div
import org.jetbrains.compose.web.dom.P
import org.jetbrains.compose.web.dom.Span
import org.jetbrains.compose.web.dom.Text
import org.w3c.fetch.RequestInit import org.w3c.fetch.RequestInit
import react.Props
import react.dom.attrs
import react.dom.button
import react.dom.p
import react.fc
import react.useMemo
import react.useState
import space.kscience.dataforge.meta.invoke import space.kscience.dataforge.meta.invoke
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.visionforge.Colors import space.kscience.visionforge.Colors
import space.kscience.visionforge.react.flexColumn import space.kscience.visionforge.compose.FlexColumn
import space.kscience.visionforge.react.flexRow import space.kscience.visionforge.compose.FlexRow
import space.kscience.visionforge.ring.ThreeCanvasWithControls
import space.kscience.visionforge.ring.tab
import space.kscience.visionforge.solid.Solids import space.kscience.visionforge.solid.Solids
import space.kscience.visionforge.solid.ambientLight import space.kscience.visionforge.solid.ambientLight
import space.kscience.visionforge.solid.edges import space.kscience.visionforge.solid.edges
import space.kscience.visionforge.solid.invoke import space.kscience.visionforge.solid.invoke
import space.kscience.visionforge.solid.specifications.Canvas3DOptions import space.kscience.visionforge.solid.specifications.Canvas3DOptions
import styled.css import space.kscience.visionforge.solid.three.compose.ThreeView
import styled.styledDiv
import styled.styledSpan
import kotlin.math.PI import kotlin.math.PI
external interface MMAppProps : Props { @Composable
var model: Model fun MMApp(solids: Solids, model: Model, selected: Name? = null) {
var solids: Solids
var selected: Name?
}
@OptIn(DelicateCoroutinesApi::class) val mmOptions = remember {
@JsExport
val MMApp = fc<MMAppProps>("Muon monitor") { props ->
val mmOptions = useMemo {
Canvas3DOptions { Canvas3DOptions {
camera { camera {
distance = 2100.0 distance = 2100.0
latitude = PI / 6 latitude = PI / 6
azimuth = PI + PI / 6 azimuth = PI + PI / 6
} }
} }
} }
val root = useMemo(props.model) { val root = remember(model) {
props.model.root.apply { model.root.apply {
edges() edges()
ambientLight{ ambientLight {
color(Colors.white) color(Colors.white)
} }
} }
} }
var events: Set<Event> by useState(emptySet()) val events = remember { mutableStateListOf<Event>() }
styledDiv { Div({
css { style {
height = 100.vh - 12.pt height(100.vh - 12.pt)
} }
child(ThreeCanvasWithControls) { }) {
attrs { ThreeView(solids, root, selected, mmOptions) {
this.solids = props.solids Tab("Events") {
this.builderOfSolid = CompletableDeferred(root)
this.selected = props.selected FlexColumn {
this.options = mmOptions FlexRow {
tab("Events") { Button("Next") {
flexColumn {
flexRow {
button {
+"Next"
attrs {
onClickFunction = {
solids.context.launch { solids.context.launch {
val event = window.fetch( val event = window.fetch(
"http://localhost:8080/event", "http://localhost:8080/event",
@ -95,187 +75,32 @@ val MMApp = fc<MMAppProps>("Muon monitor") { props ->
}.then { body -> }.then { body ->
Json.decodeFromString(Event.serializer(), body) Json.decodeFromString(Event.serializer(), body)
}.await() }.await()
events = events + event events.add(event)
props.model.displayEvent(event) model.displayEvent(event)
}
}
}
}
button {
+"Clear"
attrs {
onClickFunction = {
events = emptySet()
props.model.reset()
} }
} }
Button("Clear") {
events.clear()
model.reset()
} }
} }
} }
events.forEach { event -> events.forEach { event ->
p { P {
styledSpan { Span {
+event.id.toString() Text(event.id.toString())
} }
+" : " Text(" : ")
styledSpan { Span({
css { style {
color = Color.blue color(Color.blue)
} }
+event.hits.toString() }) {
Text(event.hits.toString())
} }
} }
} }
} }
} }
} }
}
// var selected by useState { props.selected }
//
// val onSelect: (Name?) -> Unit = {
// selected = it
// }
//
//
// gridRow {
// flexColumn {
// css {
// +"col-lg-3"
// +"order-lg-1"
// +"order-2"
// padding(0.px)
// overflowY = Overflow.auto
// height = 100.vh
// }
// //tree
// card("Object tree") {
// css {
// flex(1.0, 1.0, FlexBasis.auto)
// }
// visionTree(root, selected, onSelect)
// }
// }
// flexColumn {
// css {
// +"col-lg-6"
// +"order-lg-2"
// +"order-1"
// height = 100.vh
// }
// h1("mx-auto page-header") {
// +"Muon monitor demo"
// }
// //canvas
//
// child(ThreeCanvasComponent) {
// attrs {
// this.context = props.context
// this.solid = root
// this.selected = selected
// this.options = mmOptions
// }
// }
// }
// flexColumn {
// css {
// +"col-lg-3"
// +"order-3"
// padding(0.px)
// height = 100.vh
// }
// styledDiv {
// css {
// flex(0.0, 1.0, FlexBasis.zero)
// }
// //settings
// card("Canvas configuration") {
// canvasControls(mmOptions, root)
// }
//
// card("Events") {
// button {
// +"Next"
// attrs {
// onClickFunction = {
// GlobalScope.launch {
// val event = props.connection.get<Event>("http://localhost:8080/event")
// props.model.displayEvent(event)
// }
// }
// }
// }
// button {
// +"Clear"
// attrs {
// onClickFunction = {
// props.model.reset()
// }
// }
// }
// }
// }
// styledDiv {
// css {
// padding(0.px)
// }
// nav {
// attrs {
// attributes["aria-label"] = "breadcrumb"
// }
// ol("breadcrumb") {
// li("breadcrumb-item") {
// button(classes = "btn btn-link p-0") {
// +"World"
// attrs {
// onClickFunction = {
// selected = Name.EMPTY
// }
// }
// }
// }
// if (selected != null) {
// val tokens = ArrayList<NameToken>(selected?.length ?: 1)
// selected?.tokens?.forEach { token ->
// tokens.add(token)
// val fullName = Name(tokens.toList())
// li("breadcrumb-item") {
// button(classes = "btn btn-link p-0") {
// +token.toString()
// attrs {
// onClickFunction = {
// console.log("Selected = $fullName")
// selected = fullName
// }
// }
// }
// }
// }
// }
// }
// }
// }
// styledDiv {
// css {
// overflowY = Overflow.auto
// }
// //properties
// card("Properties") {
// selected.let { selected ->
// val selectedObject: Vision? = when {
// selected == null -> null
// selected.isEmpty() -> root
// else -> root[selected]
// }
// if (selectedObject != null) {
// visionPropertyEditor(selectedObject, key = selected)
// }
// }
// }
// }
// }
//
// }
} }

View File

@ -1,12 +1,11 @@
package ru.mipt.npm.muon.monitor package ru.mipt.npm.muon.monitor
import org.jetbrains.compose.web.renderComposable
import org.w3c.dom.Document import org.w3c.dom.Document
import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Context
import space.kscience.dataforge.context.request import space.kscience.dataforge.context.request
import space.kscience.visionforge.Application import space.kscience.visionforge.Application
import space.kscience.visionforge.VisionManager import space.kscience.visionforge.VisionManager
import space.kscience.visionforge.react.createRoot
import space.kscience.visionforge.react.render
import space.kscience.visionforge.solid.Solids import space.kscience.visionforge.solid.Solids
import space.kscience.visionforge.solid.three.ThreePlugin import space.kscience.visionforge.solid.three.ThreePlugin
import space.kscience.visionforge.startApplication import space.kscience.visionforge.startApplication
@ -24,13 +23,8 @@ private class MMDemoApp : Application {
val model = Model(visionManager) val model = Model(visionManager)
val element = document.getElementById("app") ?: error("Element with id 'app' not found on page") val element = document.getElementById("app") ?: error("Element with id 'app' not found on page")
createRoot(element).render { renderComposable(element) {
child(MMApp) { MMApp(context.request(Solids), model)
attrs {
this.model = model
this.solids = context.request(Solids)
}
}
} }
} }
} }

View File

@ -14,17 +14,10 @@ repositories {
kotlin { kotlin {
js(IR) { js(IR) {
useCommonJs()
browser { browser {
webpackTask { webpackTask {
mainOutputFileName.set("js/visionforge-playground.js") mainOutputFileName.set("js/visionforge-playground.js")
} }
commonWebpackConfig {
sourceMaps = true
cssSupport{
enabled.set(false)
}
}
} }
binaries.executable() binaries.executable()
} }
@ -57,7 +50,6 @@ kotlin {
val jsMain by getting { val jsMain by getting {
dependencies { dependencies {
implementation(projects.ui.ring)
implementation(projects.visionforgeThreejs) implementation(projects.visionforgeThreejs)
compileOnly(npm("webpack-bundle-analyzer","4.5.0")) compileOnly(npm("webpack-bundle-analyzer","4.5.0"))
} }

View File

@ -2,13 +2,13 @@ import space.kscience.dataforge.misc.DFExperimental
import space.kscience.visionforge.jupyter.VFNotebookClient import space.kscience.visionforge.jupyter.VFNotebookClient
import space.kscience.visionforge.markup.MarkupPlugin import space.kscience.visionforge.markup.MarkupPlugin
import space.kscience.visionforge.plotly.PlotlyPlugin import space.kscience.visionforge.plotly.PlotlyPlugin
import space.kscience.visionforge.ring.ThreeWithControlsPlugin
import space.kscience.visionforge.runVisionClient import space.kscience.visionforge.runVisionClient
import space.kscience.visionforge.solid.three.ThreePlugin
import space.kscience.visionforge.tables.TableVisionJsPlugin import space.kscience.visionforge.tables.TableVisionJsPlugin
@DFExperimental @DFExperimental
fun main() = runVisionClient { fun main() = runVisionClient {
plugin(ThreeWithControlsPlugin) plugin(ThreePlugin)
plugin(PlotlyPlugin) plugin(PlotlyPlugin)
plugin(MarkupPlugin) plugin(MarkupPlugin)
plugin(TableVisionJsPlugin) plugin(TableVisionJsPlugin)

View File

@ -1,3 +1,6 @@
import org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode
plugins { plugins {
id("space.kscience.gradle.mpp") id("space.kscience.gradle.mpp")
alias(spclibs.plugins.ktor) alias(spclibs.plugins.ktor)
@ -19,6 +22,8 @@ kscience {
group = "center.sciprog" group = "center.sciprog"
kotlin.explicitApi = ExplicitApiMode.Disabled
application { application {
mainClass.set("ru.mipt.npm.sat.SatServerKt") mainClass.set("ru.mipt.npm.sat.SatServerKt")
} }

View File

@ -72,7 +72,7 @@ class ThreeDemoGrid(element: Element) : VisionLayout<Solid> {
} }
} }
val element = document.getElementById("output-$name") ?: error("Element not found") val element = document.getElementById("output-$name") ?: error("Element not found")
three.getOrCreateCanvas(element, canvasOptions) ThreeCanvas(three, element, canvasOptions)
}.render(vision) }.render(vision)
} }
} }

View File

@ -45,7 +45,7 @@ include(
":ui:ring", ":ui:ring",
// ":ui:material", // ":ui:material",
":ui:bootstrap", ":ui:bootstrap",
":ui:compose", ":visionforge-compose",
":visionforge-core", ":visionforge-core",
":visionforge-solid", ":visionforge-solid",
// ":visionforge-fx", // ":visionforge-fx",

View File

@ -1,52 +0,0 @@
package space.kscience.visionforge.compose
import androidx.compose.runtime.*
import kotlinx.dom.clear
import org.jetbrains.compose.web.css.*
import org.jetbrains.compose.web.dom.Div
import space.kscience.dataforge.context.Context
import space.kscience.dataforge.context.request
import space.kscience.dataforge.names.Name
import space.kscience.visionforge.solid.Solid
import space.kscience.visionforge.solid.specifications.Canvas3DOptions
import space.kscience.visionforge.solid.three.ThreeCanvas
import space.kscience.visionforge.solid.three.ThreePlugin
@Composable
public fun ThreeCanvas(
context: Context,
options: Canvas3DOptions?,
solid: Solid?,
selected: Name?,
) {
val three: ThreePlugin by derivedStateOf { context.request(ThreePlugin) }
Div({
style {
maxWidth(100.vw)
maxHeight(100.vh)
width(100.percent)
height(100.percent)
}
}) {
var canvas: ThreeCanvas? = null
DisposableEffect(options) {
canvas = ThreeCanvas(three, scopeElement, options ?: Canvas3DOptions())
onDispose {
scopeElement.clear()
canvas = null
}
}
LaunchedEffect(solid) {
if (solid != null) {
canvas?.render(solid)
} else {
canvas?.clear()
}
}
LaunchedEffect(selected) {
canvas?.select(selected)
}
}
}

View File

@ -1,169 +0,0 @@
@file:OptIn(ExperimentalComposeWebApi::class)
package space.kscience.visionforge.compose
import androidx.compose.runtime.*
import app.softwork.bootstrapcompose.Card
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.launch
import org.jetbrains.compose.web.ExperimentalComposeWebApi
import org.jetbrains.compose.web.css.*
import org.jetbrains.compose.web.dom.*
import space.kscience.dataforge.meta.get
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.isEmpty
import space.kscience.visionforge.*
import space.kscience.visionforge.solid.Solid
import space.kscience.visionforge.solid.SolidGroup
import space.kscience.visionforge.solid.Solids
import space.kscience.visionforge.solid.specifications.Canvas3DOptions
@Composable
public fun ThreeCanvasWithControls(
solids: Solids,
builderOfSolid: Deferred<Solid?>,
initialSelected: Name?,
options: Canvas3DOptions?,
tabBuilder: @Composable TabsBuilder.() -> Unit = {},
) {
var selected: Name? by remember { mutableStateOf(initialSelected) }
var solid: Solid? by remember { mutableStateOf(null) }
LaunchedEffect(builderOfSolid) {
solids.context.launch {
solid = builderOfSolid.await()
//ensure that the solid is properly rooted
if (solid?.parent == null) {
solid?.setAsRoot(solids.context.visionManager)
}
}
}
val optionsWithSelector = remember(options) {
(options ?: Canvas3DOptions()).apply {
this.onSelect = {
selected = it
}
}
}
val selectedVision: Vision? = remember(builderOfSolid, selected) {
selected?.let {
when {
it.isEmpty() -> solid
else -> (solid as? SolidGroup)?.get(it)
}
}
}
FlexRow({
style {
height(100.percent)
width(100.percent)
flexWrap(FlexWrap.Wrap)
alignItems(AlignItems.Stretch)
alignContent(AlignContent.Stretch)
}
}) {
FlexColumn({
style {
height(100.percent)
minWidth(600.px)
flex(10, 1, 600.px)
position(Position.Relative)
}
}) {
if (solid == null) {
Div({
style {
position(Position.Fixed)
width(100.percent)
height(100.percent)
zIndex(1000)
top(40.percent)
left(0.px)
opacity(0.5)
filter {
opacity(50.percent)
}
}
}) {
Div({ classes("d-flex", " justify-content-center") }) {
Div({
classes("spinner-grow", "text-primary")
style {
width(3.cssRem)
height(3.cssRem)
zIndex(20)
}
attr("role", "status")
}) {
Span({ classes("sr-only") }) { Text("Loading 3D vision") }
}
}
}
} else {
ThreeCanvas(solids.context, optionsWithSelector, solid, selected)
}
selectedVision?.let { vision ->
Div({
style {
position(Position.Absolute)
top(5.px)
right(5.px)
width(450.px)
}
}) {
Card(
headerAttrs = {
// border = true
},
header = {
NameCrumbs(selected) { selected = it }
}
) {
PropertyEditor(
scope = solids.context,
meta = vision.properties.root(),
getPropertyState = { name ->
if (vision.properties.own?.get(name) != null) {
EditorPropertyState.Defined
} else if (vision.properties.root()[name] != null) {
// TODO differentiate
EditorPropertyState.Default()
} else {
EditorPropertyState.Undefined
}
},
updates = vision.properties.changes,
rootDescriptor = vision.descriptor
)
}
vision.styles.takeIf { it.isNotEmpty() }?.let { styles ->
P {
B { Text("Styles: ") }
Text(styles.joinToString(separator = ", "))
}
}
}
}
}
}
FlexColumn({
style {
paddingAll(4.px)
minWidth(400.px)
height(100.percent)
overflowY("auto")
flex(1, 10, 300.px)
}
}) {
ThreeControls(solid, optionsWithSelector, selected, onSelect = { selected = it }, tabBuilder = tabBuilder)
}
}

View File

@ -7,8 +7,6 @@ import space.kscience.dataforge.context.Context
import space.kscience.dataforge.context.PluginFactory import space.kscience.dataforge.context.PluginFactory
import space.kscience.dataforge.context.PluginTag import space.kscience.dataforge.context.PluginTag
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.boolean
import space.kscience.dataforge.meta.get
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.asName
import space.kscience.visionforge.ElementVisionRenderer import space.kscience.visionforge.ElementVisionRenderer
@ -28,9 +26,6 @@ public class ThreeWithControlsPlugin : AbstractPlugin(), ElementVisionRenderer {
if (vision is Solid) ElementVisionRenderer.DEFAULT_RATING * 2 else ElementVisionRenderer.ZERO_RATING if (vision is Solid) ElementVisionRenderer.DEFAULT_RATING * 2 else ElementVisionRenderer.ZERO_RATING
override fun render(element: Element, client: VisionClient, name: Name, vision: Vision, meta: Meta) { override fun render(element: Element, client: VisionClient, name: Name, vision: Vision, meta: Meta) {
if (meta["controls.enabled"].boolean == false) {
three.render(element, client, name, vision, meta)
} else {
space.kscience.visionforge.react.createRoot(element).render { space.kscience.visionforge.react.createRoot(element).render {
child(ThreeCanvasWithControls) { child(ThreeCanvasWithControls) {
attrs { attrs {
@ -41,7 +36,6 @@ public class ThreeWithControlsPlugin : AbstractPlugin(), ElementVisionRenderer {
} }
} }
} }
}
override fun content(target: String): Map<Name, Any> { override fun content(target: String): Map<Name, Any> {
return when (target) { return when (target) {

View File

@ -2,8 +2,6 @@
plugins { plugins {
id("space.kscience.gradle.mpp") id("space.kscience.gradle.mpp")
alias(spclibs.plugins.compose) alias(spclibs.plugins.compose)
// id("org.jetbrains.compose") version "1.5.11"
// id("com.android.library")
} }
kscience{ kscience{
@ -15,9 +13,9 @@ kscience{
kotlin { kotlin {
// android() // android()
sourceSets { sourceSets {
val commonMain by getting { commonMain{
dependencies { dependencies{
api(projects.visionforgeCore)
} }
} }
@ -35,7 +33,6 @@ kotlin {
api(compose.html.core) api(compose.html.core)
api("app.softwork:bootstrap-compose:0.1.15") api("app.softwork:bootstrap-compose:0.1.15")
api("app.softwork:bootstrap-compose-icons:0.1.15") api("app.softwork:bootstrap-compose-icons:0.1.15")
api(projects.visionforge.visionforgeThreejs)
} }
} }
} }

View File

@ -10,7 +10,6 @@ import org.jetbrains.compose.web.dom.Text
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.descriptors.MetaDescriptor import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.meta.descriptors.get import space.kscience.dataforge.meta.descriptors.get
import space.kscience.dataforge.meta.get
import space.kscience.dataforge.meta.isLeaf import space.kscience.dataforge.meta.isLeaf
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.NameToken import space.kscience.dataforge.names.NameToken

View File

@ -0,0 +1,49 @@
package space.kscience.visionforge.compose
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import kotlinx.dom.clear
import org.jetbrains.compose.web.dom.AttrBuilderContext
import org.jetbrains.compose.web.dom.Div
import org.w3c.dom.HTMLDivElement
import space.kscience.dataforge.context.gather
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.asName
import space.kscience.visionforge.ElementVisionRenderer
import space.kscience.visionforge.Vision
import space.kscience.visionforge.VisionClient
/**
* Render an Element vision via injected vision renderer inside compose-html
*/
@Composable
public fun Vision(
client: VisionClient,
vision: Vision,
name: Name = "@vision[${vision.hashCode().toString(16)}]".asName(),
meta: Meta = Meta.EMPTY,
attrs: AttrBuilderContext<HTMLDivElement>? = null,
): Unit = Div(attrs) {
val renderer by derivedStateOf {
client.context.gather<ElementVisionRenderer>(ElementVisionRenderer.TYPE).values.mapNotNull {
val rating = it.rateVision(vision)
if (rating > 0) {
rating to it
} else {
null
}
}.maxBy { it.first }.second
}
DisposableEffect(vision, name, renderer, meta) {
renderer.render(scopeElement, client, name, vision, meta)
onDispose {
scopeElement.clear()
}
}
}

View File

@ -20,7 +20,6 @@ import space.kscience.dataforge.meta.descriptors.ValueRestriction
import space.kscience.dataforge.meta.descriptors.allowedValues import space.kscience.dataforge.meta.descriptors.allowedValues
import space.kscience.visionforge.Colors import space.kscience.visionforge.Colors
import space.kscience.visionforge.widgetType import space.kscience.visionforge.widgetType
import three.math.Color
@Composable @Composable
@ -151,7 +150,8 @@ public fun ColorValueChooser(
value( value(
value?.let { value -> value?.let { value ->
if (value.type == ValueType.NUMBER) Colors.rgbToString(value.int) if (value.type == ValueType.NUMBER) Colors.rgbToString(value.int)
else "#" + Color(value.string).getHexString() else value.string
//else "#" + Color(value.string).getHexString()
} ?: "#000000" } ?: "#000000"
) )
onChange { onChange {

View File

@ -225,6 +225,8 @@ public abstract interface class space/kscience/visionforge/ControlVision : space
public final class space/kscience/visionforge/ControlVisionKt { public final class space/kscience/visionforge/ControlVisionKt {
public static final fun VisionClickEvent (Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/visionforge/VisionClickEvent; public static final fun VisionClickEvent (Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/visionforge/VisionClickEvent;
public static synthetic fun VisionClickEvent$default (Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/visionforge/VisionClickEvent; public static synthetic fun VisionClickEvent$default (Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/visionforge/VisionClickEvent;
public static final fun VisionInputEvent (Lspace/kscience/dataforge/meta/Value;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/visionforge/VisionInputEvent;
public static synthetic fun VisionInputEvent$default (Lspace/kscience/dataforge/meta/Value;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/visionforge/VisionInputEvent;
public static final fun VisionValueChangeEvent (Lspace/kscience/dataforge/meta/Value;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/visionforge/VisionValueChangeEvent; public static final fun VisionValueChangeEvent (Lspace/kscience/dataforge/meta/Value;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/visionforge/VisionValueChangeEvent;
public static synthetic fun VisionValueChangeEvent$default (Lspace/kscience/dataforge/meta/Value;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/visionforge/VisionValueChangeEvent; public static synthetic fun VisionValueChangeEvent$default (Lspace/kscience/dataforge/meta/Value;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/visionforge/VisionValueChangeEvent;
public static final fun onClick (Lspace/kscience/visionforge/ClickControl;Lkotlinx/coroutines/CoroutineScope;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/Job; public static final fun onClick (Lspace/kscience/visionforge/ClickControl;Lkotlinx/coroutines/CoroutineScope;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/Job;
@ -495,6 +497,7 @@ public final class space/kscience/visionforge/VisionClientKt {
public static final fun notifyPropertyChanged (Lspace/kscience/visionforge/VisionClient;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Ljava/lang/String;)V public static final fun notifyPropertyChanged (Lspace/kscience/visionforge/VisionClient;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Ljava/lang/String;)V
public static final fun notifyPropertyChanged (Lspace/kscience/visionforge/VisionClient;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lspace/kscience/dataforge/meta/Meta;)V public static final fun notifyPropertyChanged (Lspace/kscience/visionforge/VisionClient;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lspace/kscience/dataforge/meta/Meta;)V
public static final fun notifyPropertyChanged (Lspace/kscience/visionforge/VisionClient;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Z)V public static final fun notifyPropertyChanged (Lspace/kscience/visionforge/VisionClient;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Z)V
public static final fun sendEventAsync (Lspace/kscience/visionforge/VisionClient;Lspace/kscience/dataforge/names/Name;Lspace/kscience/visionforge/VisionEvent;)Lkotlinx/coroutines/Job;
} }
public abstract interface class space/kscience/visionforge/VisionContainer { public abstract interface class space/kscience/visionforge/VisionContainer {
@ -560,6 +563,30 @@ public final class space/kscience/visionforge/VisionGroupKt {
public static synthetic fun group$default (Lspace/kscience/visionforge/MutableVisionContainer;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lspace/kscience/visionforge/SimpleVisionGroup; public static synthetic fun group$default (Lspace/kscience/visionforge/MutableVisionContainer;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lspace/kscience/visionforge/SimpleVisionGroup;
} }
public final class space/kscience/visionforge/VisionInputEvent : space/kscience/visionforge/VisionControlEvent {
public static final field Companion Lspace/kscience/visionforge/VisionInputEvent$Companion;
public fun <init> (Lspace/kscience/dataforge/meta/Meta;)V
public fun getMeta ()Lspace/kscience/dataforge/meta/Meta;
public final fun getName ()Lspace/kscience/dataforge/names/Name;
public final fun getValue ()Lspace/kscience/dataforge/meta/Value;
public fun toString ()Ljava/lang/String;
}
public final class space/kscience/visionforge/VisionInputEvent$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
public static final field INSTANCE Lspace/kscience/visionforge/VisionInputEvent$$serializer;
public fun childSerializers ()[Lkotlinx/serialization/KSerializer;
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lspace/kscience/visionforge/VisionInputEvent;
public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lspace/kscience/visionforge/VisionInputEvent;)V
public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
}
public final class space/kscience/visionforge/VisionInputEvent$Companion {
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
public final class space/kscience/visionforge/VisionKt { public final class space/kscience/visionforge/VisionKt {
public static final fun getVisible (Lspace/kscience/visionforge/Vision;)Ljava/lang/Boolean; public static final fun getVisible (Lspace/kscience/visionforge/Vision;)Ljava/lang/Boolean;
public static final fun onPropertyChange (Lspace/kscience/visionforge/Vision;Lkotlinx/coroutines/CoroutineScope;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/Job; public static final fun onPropertyChange (Lspace/kscience/visionforge/Vision;Lkotlinx/coroutines/CoroutineScope;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/Job;
@ -649,8 +676,8 @@ public final class space/kscience/visionforge/VisionPropertiesKt {
public static final fun get (Lspace/kscience/visionforge/VisionProperties;Ljava/lang/String;Ljava/lang/Boolean;Ljava/lang/Boolean;)Lspace/kscience/dataforge/meta/Meta; public static final fun get (Lspace/kscience/visionforge/VisionProperties;Ljava/lang/String;Ljava/lang/Boolean;Ljava/lang/Boolean;)Lspace/kscience/dataforge/meta/Meta;
public static synthetic fun get$default (Lspace/kscience/visionforge/MutableVisionProperties;Ljava/lang/String;Ljava/lang/Boolean;Ljava/lang/Boolean;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMeta; public static synthetic fun get$default (Lspace/kscience/visionforge/MutableVisionProperties;Ljava/lang/String;Ljava/lang/Boolean;Ljava/lang/Boolean;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMeta;
public static synthetic fun get$default (Lspace/kscience/visionforge/VisionProperties;Ljava/lang/String;Ljava/lang/Boolean;Ljava/lang/Boolean;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/Meta; public static synthetic fun get$default (Lspace/kscience/visionforge/VisionProperties;Ljava/lang/String;Ljava/lang/Boolean;Ljava/lang/Boolean;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/Meta;
public static final fun getValue (Lspace/kscience/visionforge/VisionProperties;Ljava/lang/String;Ljava/lang/Boolean;Ljava/lang/Boolean;)Lspace/kscience/dataforge/meta/Value; public static final fun getValue (Lspace/kscience/visionforge/VisionProperties;Ljava/lang/String;ZLjava/lang/Boolean;)Lspace/kscience/dataforge/meta/Value;
public static synthetic fun getValue$default (Lspace/kscience/visionforge/VisionProperties;Ljava/lang/String;Ljava/lang/Boolean;Ljava/lang/Boolean;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/Value; public static synthetic fun getValue$default (Lspace/kscience/visionforge/VisionProperties;Ljava/lang/String;ZLjava/lang/Boolean;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/Value;
public static final fun invoke (Lspace/kscience/visionforge/MutableVisionProperties;Lkotlin/jvm/functions/Function1;)V public static final fun invoke (Lspace/kscience/visionforge/MutableVisionProperties;Lkotlin/jvm/functions/Function1;)V
public static final fun remove (Lspace/kscience/visionforge/MutableVisionProperties;Ljava/lang/String;)V public static final fun remove (Lspace/kscience/visionforge/MutableVisionProperties;Ljava/lang/String;)V
public static final fun remove (Lspace/kscience/visionforge/MutableVisionProperties;Lspace/kscience/dataforge/names/Name;)V public static final fun remove (Lspace/kscience/visionforge/MutableVisionProperties;Lspace/kscience/dataforge/names/Name;)V
@ -781,8 +808,8 @@ public abstract class space/kscience/visionforge/html/VisionOfHtml : space/kscie
public static final field Companion Lspace/kscience/visionforge/html/VisionOfHtml$Companion; public static final field Companion Lspace/kscience/visionforge/html/VisionOfHtml$Companion;
public fun <init> ()V public fun <init> ()V
public synthetic fun <init> (ILspace/kscience/dataforge/meta/MutableMeta;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V public synthetic fun <init> (ILspace/kscience/dataforge/meta/MutableMeta;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V
public final fun getClasses ()Ljava/util/List; public final fun getClasses ()Ljava/util/Set;
public final fun setClasses (Ljava/util/List;)V public final fun setClasses (Ljava/util/Set;)V
public static final synthetic fun write$Self (Lspace/kscience/visionforge/html/VisionOfHtml;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V public static final synthetic fun write$Self (Lspace/kscience/visionforge/html/VisionOfHtml;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V
} }
@ -813,9 +840,16 @@ public final class space/kscience/visionforge/html/VisionOfHtmlButton$Companion
} }
public abstract class space/kscience/visionforge/html/VisionOfHtmlControl : space/kscience/visionforge/html/VisionOfHtml, space/kscience/visionforge/ControlVision { public abstract class space/kscience/visionforge/html/VisionOfHtmlControl : space/kscience/visionforge/html/VisionOfHtml, space/kscience/visionforge/ControlVision {
public static final field Companion Lspace/kscience/visionforge/html/VisionOfHtmlControl$Companion;
public fun <init> ()V public fun <init> ()V
public synthetic fun <init> (ILspace/kscience/dataforge/meta/MutableMeta;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V
public fun dispatchControlEvent (Lspace/kscience/visionforge/VisionControlEvent;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun dispatchControlEvent (Lspace/kscience/visionforge/VisionControlEvent;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun getControlEventFlow ()Lkotlinx/coroutines/flow/SharedFlow; public fun getControlEventFlow ()Lkotlinx/coroutines/flow/SharedFlow;
public static final synthetic fun write$Self (Lspace/kscience/visionforge/html/VisionOfHtmlControl;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V
}
public final class space/kscience/visionforge/html/VisionOfHtmlControl$Companion {
public final fun serializer ()Lkotlinx/serialization/KSerializer;
} }
public final class space/kscience/visionforge/html/VisionOfHtmlForm : space/kscience/visionforge/html/VisionOfHtmlControl { public final class space/kscience/visionforge/html/VisionOfHtmlForm : space/kscience/visionforge/html/VisionOfHtmlControl {
@ -849,11 +883,9 @@ public final class space/kscience/visionforge/html/VisionOfHtmlFormKt {
public class space/kscience/visionforge/html/VisionOfHtmlInput : space/kscience/visionforge/html/VisionOfHtmlControl { public class space/kscience/visionforge/html/VisionOfHtmlInput : space/kscience/visionforge/html/VisionOfHtmlControl {
public static final field Companion Lspace/kscience/visionforge/html/VisionOfHtmlInput$Companion; public static final field Companion Lspace/kscience/visionforge/html/VisionOfHtmlInput$Companion;
public synthetic fun <init> (ILjava/lang/String;Lspace/kscience/visionforge/html/InputFeedbackMode;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V public synthetic fun <init> (ILspace/kscience/dataforge/meta/MutableMeta;Ljava/lang/String;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V
public fun <init> (Ljava/lang/String;Lspace/kscience/visionforge/html/InputFeedbackMode;)V public fun <init> (Ljava/lang/String;)V
public synthetic fun <init> (Ljava/lang/String;Lspace/kscience/visionforge/html/InputFeedbackMode;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getDisabled ()Z public final fun getDisabled ()Z
public final fun getFeedbackMode ()Lspace/kscience/visionforge/html/InputFeedbackMode;
public final fun getFieldName ()Ljava/lang/String; public final fun getFieldName ()Ljava/lang/String;
public final fun getInputType ()Ljava/lang/String; public final fun getInputType ()Ljava/lang/String;
public final fun getValue ()Lspace/kscience/dataforge/meta/Value; public final fun getValue ()Lspace/kscience/dataforge/meta/Value;
@ -891,6 +923,8 @@ public final class space/kscience/visionforge/html/VisionOfHtmlKt {
public static synthetic fun htmlRangeField$default (Lspace/kscience/visionforge/html/VisionOutput;Ljava/lang/Number;Ljava/lang/Number;Ljava/lang/Number;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lspace/kscience/visionforge/html/VisionOfRangeField; public static synthetic fun htmlRangeField$default (Lspace/kscience/visionforge/html/VisionOutput;Ljava/lang/Number;Ljava/lang/Number;Ljava/lang/Number;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lspace/kscience/visionforge/html/VisionOfRangeField;
public static final fun htmlTextField (Lspace/kscience/visionforge/html/VisionOutput;Lkotlin/jvm/functions/Function1;)Lspace/kscience/visionforge/html/VisionOfTextField; public static final fun htmlTextField (Lspace/kscience/visionforge/html/VisionOutput;Lkotlin/jvm/functions/Function1;)Lspace/kscience/visionforge/html/VisionOfTextField;
public static synthetic fun htmlTextField$default (Lspace/kscience/visionforge/html/VisionOutput;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lspace/kscience/visionforge/html/VisionOfTextField; public static synthetic fun htmlTextField$default (Lspace/kscience/visionforge/html/VisionOutput;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lspace/kscience/visionforge/html/VisionOfTextField;
public static final fun onInput (Lspace/kscience/visionforge/html/VisionOfHtmlInput;Lkotlinx/coroutines/CoroutineScope;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/Job;
public static synthetic fun onInput$default (Lspace/kscience/visionforge/html/VisionOfHtmlInput;Lkotlinx/coroutines/CoroutineScope;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/Job;
public static final fun onValueChange (Lspace/kscience/visionforge/html/VisionOfHtmlInput;Lkotlinx/coroutines/CoroutineScope;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/Job; public static final fun onValueChange (Lspace/kscience/visionforge/html/VisionOfHtmlInput;Lkotlinx/coroutines/CoroutineScope;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/Job;
public static synthetic fun onValueChange$default (Lspace/kscience/visionforge/html/VisionOfHtmlInput;Lkotlinx/coroutines/CoroutineScope;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/Job; public static synthetic fun onValueChange$default (Lspace/kscience/visionforge/html/VisionOfHtmlInput;Lkotlinx/coroutines/CoroutineScope;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/Job;
} }

View File

@ -83,9 +83,32 @@ public class VisionValueChangeEvent(override val meta: Meta) : VisionControlEven
override fun toString(): String = meta.toString() override fun toString(): String = meta.toString()
} }
public fun VisionValueChangeEvent(value: Value?, name: Name? = null): VisionValueChangeEvent = VisionValueChangeEvent( public fun VisionValueChangeEvent(value: Value?, name: Name? = null): VisionValueChangeEvent = VisionValueChangeEvent(
Meta { Meta {
this.value = value this.value = value
name?.let { set("name", it.toString()) } name?.let { set("name", it.toString()) }
} }
) )
@Serializable
@SerialName("control.input")
public class VisionInputEvent(override val meta: Meta) : VisionControlEvent() {
public val value: Value? get() = meta.value
/**
* The name of a control that fired the event
*/
public val name: Name? get() = meta["name"]?.string?.parseAsName()
override fun toString(): String = meta.toString()
}
public fun VisionInputEvent(value: Value?, name: Name? = null): VisionInputEvent = VisionInputEvent(
Meta {
this.value = value
name?.let { set("name", it.toString()) }
}
)

View File

@ -1,5 +1,7 @@
package space.kscience.visionforge package space.kscience.visionforge
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import space.kscience.dataforge.context.Plugin import space.kscience.dataforge.context.Plugin
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
@ -16,6 +18,10 @@ public interface VisionClient: Plugin {
public fun notifyPropertyChanged(visionName: Name, propertyName: Name, item: Meta?) public fun notifyPropertyChanged(visionName: Name, propertyName: Name, item: Meta?)
} }
public fun VisionClient.sendEventAsync(targetName: Name, event: VisionEvent): Job = context.launch {
sendEvent(targetName, event)
}
public fun VisionClient.notifyPropertyChanged(visionName: Name, propertyName: String, item: Meta?) { public fun VisionClient.notifyPropertyChanged(visionName: Name, propertyName: String, item: Meta?) {
notifyPropertyChanged(visionName, propertyName.parseAsName(true), item) notifyPropertyChanged(visionName, propertyName.parseAsName(true), item)
} }

View File

@ -85,6 +85,7 @@ public class VisionManager(meta: Meta) : AbstractPlugin(meta), MutableVisionCont
subclass(VisionMetaEvent.serializer()) subclass(VisionMetaEvent.serializer())
subclass(VisionClickEvent.serializer()) subclass(VisionClickEvent.serializer())
subclass(VisionValueChangeEvent.serializer()) subclass(VisionValueChangeEvent.serializer())
subclass(VisionInputEvent.serializer())
} }
} }

View File

@ -265,14 +265,14 @@ public abstract class AbstractVisionProperties(
public fun VisionProperties.getValue( public fun VisionProperties.getValue(
name: String, name: String,
inherit: Boolean? = null, inherit: Boolean,
includeStyles: Boolean? = null, includeStyles: Boolean? = null,
): Value? = getValue(name.parseAsName(), inherit, includeStyles) ): Value? = getValue(name.parseAsName(), inherit, includeStyles)
/** /**
* Get [Vision] property using key as a String * Get [Vision] property using key as a String
*/ */
public fun VisionProperties.get( public operator fun VisionProperties.get(
name: String, name: String,
inherit: Boolean? = null, inherit: Boolean? = null,
includeStyles: Boolean? = null, includeStyles: Boolean? = null,
@ -292,7 +292,7 @@ public fun MutableVisionProperties.root(
/** /**
* Get [Vision] property using key as a String * Get [Vision] property using key as a String
*/ */
public fun MutableVisionProperties.get( public operator fun MutableVisionProperties.get(
name: String, name: String,
inherit: Boolean? = null, inherit: Boolean? = null,
includeStyles: Boolean? = null, includeStyles: Boolean? = null,

View File

@ -12,15 +12,16 @@ import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient import kotlinx.serialization.Transient
import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.*
import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.asName
import space.kscience.visionforge.AbstractVision import space.kscience.visionforge.*
import space.kscience.visionforge.ControlVision
import space.kscience.visionforge.VisionControlEvent
import space.kscience.visionforge.VisionValueChangeEvent
@Serializable @Serializable
public abstract class VisionOfHtml : AbstractVision() { public abstract class VisionOfHtml : AbstractVision() {
public var classes: List<String> by properties.stringList(*emptyArray()) public var classes: Set<String>
get() = properties.get(::classes.name,false).stringList?.toSet() ?: emptySet()
set(value) {
properties[::classes.name] = value.map { it.asValue() }
}
} }
@Serializable @Serializable
@ -58,6 +59,7 @@ public enum class InputFeedbackMode {
NONE NONE
} }
@Serializable
public abstract class VisionOfHtmlControl: VisionOfHtml(), ControlVision{ public abstract class VisionOfHtmlControl: VisionOfHtml(), ControlVision{
@Transient @Transient
@ -76,7 +78,6 @@ public abstract class VisionOfHtmlControl: VisionOfHtml(), ControlVision{
@SerialName("html.input") @SerialName("html.input")
public open class VisionOfHtmlInput( public open class VisionOfHtmlInput(
public val inputType: String, public val inputType: String,
public val feedbackMode: InputFeedbackMode = InputFeedbackMode.ONCHANGE,
) : VisionOfHtmlControl() { ) : VisionOfHtmlControl() {
public var value: Value? by properties.value() public var value: Value? by properties.value()
public var disabled: Boolean by properties.boolean { false } public var disabled: Boolean by properties.boolean { false }
@ -92,6 +93,11 @@ public fun VisionOfHtmlInput.onValueChange(
callback: suspend VisionValueChangeEvent.() -> Unit, callback: suspend VisionValueChangeEvent.() -> Unit,
): Job = controlEventFlow.filterIsInstance<VisionValueChangeEvent>().onEach(callback).launchIn(scope) ): Job = controlEventFlow.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)
@Suppress("UnusedReceiverParameter") @Suppress("UnusedReceiverParameter")
public inline fun VisionOutput.htmlInput( public inline fun VisionOutput.htmlInput(
inputType: String, inputType: String,

View File

@ -40,7 +40,7 @@ internal class VisionPropertyTest {
@Test @Test
fun testPropertyEdit() { fun testPropertyEdit() {
val vision = manager.group() val vision = manager.group()
vision.properties.get("fff.ddd").apply { vision.properties["fff.ddd"].apply {
value = 2.asValue() value = 2.asValue()
} }
assertEquals(2, vision.properties.getValue("fff.ddd")?.int) assertEquals(2, vision.properties.getValue("fff.ddd")?.int)
@ -50,7 +50,7 @@ internal class VisionPropertyTest {
@Test @Test
fun testPropertyUpdate() { fun testPropertyUpdate() {
val vision = manager.group() val vision = manager.group()
vision.properties.get("fff").updateWith(TestScheme) { vision.properties["fff"].updateWith(TestScheme) {
ddd = 2 ddd = 2
} }
assertEquals(2, vision.properties.getValue("fff.ddd")?.int) assertEquals(2, vision.properties.getValue("fff.ddd")?.int)

View File

@ -1,18 +1,14 @@
package space.kscience.visionforge package space.kscience.visionforge
import kotlinx.coroutines.launch
import kotlinx.dom.clear import kotlinx.dom.clear
import kotlinx.html.InputType import kotlinx.html.InputType
import kotlinx.html.div import kotlinx.html.div
import kotlinx.html.js.input import kotlinx.html.js.input
import org.w3c.dom.HTMLElement import org.w3c.dom.HTMLElement
import org.w3c.dom.HTMLInputElement import org.w3c.dom.HTMLInputElement
import org.w3c.dom.events.Event
import space.kscience.dataforge.meta.Value
import space.kscience.dataforge.meta.asValue import space.kscience.dataforge.meta.asValue
import space.kscience.dataforge.meta.double import space.kscience.dataforge.meta.double
import space.kscience.dataforge.meta.string import space.kscience.dataforge.meta.string
import space.kscience.dataforge.names.Name
import space.kscience.visionforge.html.* import space.kscience.visionforge.html.*
/** /**
@ -26,13 +22,6 @@ internal fun HTMLElement.subscribeToVision(vision: VisionOfHtml) {
} }
} }
private fun VisionClient.sendInputEvent(name: Name, value: Value?) {
context.launch {
sendEvent(name, VisionValueChangeEvent(value, name))
}
}
/** /**
* Subscribes the HTML input element to a given vision. * Subscribes the HTML input element to a given vision.
* *
@ -62,16 +51,13 @@ internal val inputVisionRenderer: ElementVisionRenderer = ElementVisionRenderer<
input { input {
type = InputType.text type = InputType.text
}.also { htmlInputElement -> }.also { htmlInputElement ->
val onEvent: (Event) -> Unit = {
client.sendInputEvent(name, htmlInputElement.value.asValue()) htmlInputElement.onchange = {
client.sendEventAsync(name, VisionValueChangeEvent(htmlInputElement.value.asValue(), name))
} }
htmlInputElement.oninput = {
when (vision.feedbackMode) { client.sendEventAsync(name, VisionInputEvent(htmlInputElement.value.asValue(), name))
InputFeedbackMode.ONCHANGE -> htmlInputElement.onchange = onEvent
InputFeedbackMode.ONINPUT -> htmlInputElement.oninput = onEvent
InputFeedbackMode.NONE -> {}
} }
htmlInputElement.subscribeToInput(vision) htmlInputElement.subscribeToInput(vision)
@ -86,18 +72,16 @@ internal val checkboxVisionRenderer: ElementVisionRenderer =
input { input {
type = InputType.checkBox type = InputType.checkBox
}.also { htmlInputElement -> }.also { htmlInputElement ->
val onEvent: (Event) -> Unit = {
client.sendInputEvent(name, htmlInputElement.checked.asValue()) htmlInputElement.onchange = {
client.sendEventAsync(name, VisionValueChangeEvent(htmlInputElement.value.asValue(), name))
} }
htmlInputElement.oninput = {
when (vision.feedbackMode) { client.sendEventAsync(name, VisionInputEvent(htmlInputElement.value.asValue(), name))
InputFeedbackMode.ONCHANGE -> htmlInputElement.onchange = onEvent
InputFeedbackMode.ONINPUT -> htmlInputElement.oninput = onEvent
InputFeedbackMode.NONE -> {}
} }
htmlInputElement.subscribeToInput(vision) htmlInputElement.subscribeToInput(vision)
vision.useProperty(VisionOfCheckbox::checked) { vision.useProperty(VisionOfCheckbox::checked) {
htmlInputElement.checked = it ?: false htmlInputElement.checked = it ?: false
@ -110,16 +94,13 @@ internal val textVisionRenderer: ElementVisionRenderer =
input { input {
type = InputType.text type = InputType.text
}.also { htmlInputElement -> }.also { htmlInputElement ->
val onEvent: (Event) -> Unit = {
client.sendInputEvent(name, htmlInputElement.value.asValue()) htmlInputElement.onchange = {
client.sendEventAsync(name, VisionValueChangeEvent(htmlInputElement.value.asValue(), name))
} }
htmlInputElement.oninput = {
when (vision.feedbackMode) { client.sendEventAsync(name, VisionInputEvent(htmlInputElement.value.asValue(), name))
InputFeedbackMode.ONCHANGE -> htmlInputElement.onchange = onEvent
InputFeedbackMode.ONINPUT -> htmlInputElement.oninput = onEvent
InputFeedbackMode.NONE -> {}
} }
htmlInputElement.subscribeToInput(vision) htmlInputElement.subscribeToInput(vision)
@ -135,18 +116,19 @@ internal val numberVisionRenderer: ElementVisionRenderer =
type = InputType.number type = InputType.number
}.also { htmlInputElement -> }.also { htmlInputElement ->
val onEvent: (Event) -> Unit = { htmlInputElement.onchange = {
htmlInputElement.value.toDoubleOrNull()?.let { htmlInputElement.value.toDoubleOrNull()?.let {
client.sendInputEvent(name, htmlInputElement.value.asValue()) client.sendEventAsync(name, VisionValueChangeEvent(it.asValue(), name))
} }
} }
when (vision.feedbackMode) { htmlInputElement.oninput = {
InputFeedbackMode.ONCHANGE -> htmlInputElement.onchange = onEvent htmlInputElement.value.toDoubleOrNull()?.let {
client.sendEventAsync(name, VisionInputEvent(it.asValue(), name))
InputFeedbackMode.ONINPUT -> htmlInputElement.oninput = onEvent
InputFeedbackMode.NONE -> {}
} }
}
htmlInputElement.subscribeToInput(vision) htmlInputElement.subscribeToInput(vision)
vision.useProperty(VisionOfNumberField::value) { vision.useProperty(VisionOfNumberField::value) {
htmlInputElement.valueAsNumber = it?.double ?: 0.0 htmlInputElement.valueAsNumber = it?.double ?: 0.0
@ -163,18 +145,18 @@ internal val rangeVisionRenderer: ElementVisionRenderer =
step = vision.step.toString() step = vision.step.toString()
}.also { htmlInputElement -> }.also { htmlInputElement ->
val onEvent: (Event) -> Unit = { htmlInputElement.onchange = {
htmlInputElement.value.toDoubleOrNull()?.let { htmlInputElement.value.toDoubleOrNull()?.let {
client.sendInputEvent(name, htmlInputElement.value.asValue()) client.sendEventAsync(name, VisionValueChangeEvent(it.asValue(), name))
} }
} }
when (vision.feedbackMode) { htmlInputElement.oninput = {
InputFeedbackMode.ONCHANGE -> htmlInputElement.onchange = onEvent htmlInputElement.value.toDoubleOrNull()?.let {
client.sendEventAsync(name, VisionInputEvent(it.asValue(), name))
InputFeedbackMode.ONINPUT -> htmlInputElement.oninput = onEvent
InputFeedbackMode.NONE -> {}
} }
}
htmlInputElement.subscribeToInput(vision) htmlInputElement.subscribeToInput(vision)
vision.useProperty(VisionOfRangeField::value) { vision.useProperty(VisionOfRangeField::value) {
htmlInputElement.valueAsNumber = it?.double ?: 0.0 htmlInputElement.valueAsNumber = it?.double ?: 0.0

View File

@ -7,16 +7,7 @@ description = "Jupyter api artifact including all common modules"
kscience { kscience {
fullStack( fullStack(
"js/visionforge-jupyter-common.js", "js/visionforge-jupyter-common.js",
jsConfig = { useCommonJs() } )
) {
commonWebpackConfig {
sourceMaps = false
cssSupport {
enabled.set(false)
}
}
}
dependencies { dependencies {
api(projects.visionforgeSolid) api(projects.visionforgeSolid)
api(projects.visionforgePlotly) api(projects.visionforgePlotly)
@ -30,7 +21,6 @@ kscience {
} }
jsMain { jsMain {
implementation(projects.ui.ring)
implementation(projects.visionforgeThreejs) implementation(projects.visionforgeThreejs)
} }

View File

@ -3,12 +3,12 @@ package space.kscience.visionforge.gdml.jupyter
import space.kscience.visionforge.jupyter.VFNotebookClient import space.kscience.visionforge.jupyter.VFNotebookClient
import space.kscience.visionforge.markup.MarkupPlugin import space.kscience.visionforge.markup.MarkupPlugin
import space.kscience.visionforge.plotly.PlotlyPlugin import space.kscience.visionforge.plotly.PlotlyPlugin
import space.kscience.visionforge.ring.ThreeWithControlsPlugin
import space.kscience.visionforge.runVisionClient import space.kscience.visionforge.runVisionClient
import space.kscience.visionforge.solid.three.ThreePlugin
import space.kscience.visionforge.tables.TableVisionJsPlugin import space.kscience.visionforge.tables.TableVisionJsPlugin
public fun main(): Unit = runVisionClient { public fun main(): Unit = runVisionClient {
plugin(ThreeWithControlsPlugin) plugin(ThreePlugin)
plugin(PlotlyPlugin) plugin(PlotlyPlugin)
plugin(MarkupPlugin) plugin(MarkupPlugin)
plugin(TableVisionJsPlugin) plugin(TableVisionJsPlugin)

View File

@ -1141,14 +1141,14 @@ public final class space/kscience/visionforge/solid/specifications/Canvas3DOptio
public final fun getAxes ()Lspace/kscience/visionforge/solid/specifications/AxesScheme; public final fun getAxes ()Lspace/kscience/visionforge/solid/specifications/AxesScheme;
public final fun getCamera ()Lspace/kscience/visionforge/solid/specifications/CameraScheme; public final fun getCamera ()Lspace/kscience/visionforge/solid/specifications/CameraScheme;
public final fun getClipping ()Lspace/kscience/visionforge/solid/specifications/PointScheme; public final fun getClipping ()Lspace/kscience/visionforge/solid/specifications/PointScheme;
public final fun getControls ()Lspace/kscience/visionforge/solid/specifications/ControlsScheme; public final fun getControls ()Lspace/kscience/visionforge/solid/specifications/Canvas3DUIScheme;
public final fun getLayers ()Ljava/util/List; public final fun getLayers ()Ljava/util/List;
public final fun getOnSelect ()Lkotlin/jvm/functions/Function1; public final fun getOnSelect ()Lkotlin/jvm/functions/Function1;
public final fun getSize ()Lspace/kscience/visionforge/solid/specifications/CanvasSize; public final fun getSize ()Lspace/kscience/visionforge/solid/specifications/CanvasSize;
public final fun setAxes (Lspace/kscience/visionforge/solid/specifications/AxesScheme;)V public final fun setAxes (Lspace/kscience/visionforge/solid/specifications/AxesScheme;)V
public final fun setCamera (Lspace/kscience/visionforge/solid/specifications/CameraScheme;)V public final fun setCamera (Lspace/kscience/visionforge/solid/specifications/CameraScheme;)V
public final fun setClipping (Lspace/kscience/visionforge/solid/specifications/PointScheme;)V public final fun setClipping (Lspace/kscience/visionforge/solid/specifications/PointScheme;)V
public final fun setControls (Lspace/kscience/visionforge/solid/specifications/ControlsScheme;)V public final fun setControls (Lspace/kscience/visionforge/solid/specifications/Canvas3DUIScheme;)V
public final fun setLayers (Ljava/util/List;)V public final fun setLayers (Ljava/util/List;)V
public final fun setOnSelect (Lkotlin/jvm/functions/Function1;)V public final fun setOnSelect (Lkotlin/jvm/functions/Function1;)V
public final fun setSize (Lspace/kscience/visionforge/solid/specifications/CanvasSize;)V public final fun setSize (Lspace/kscience/visionforge/solid/specifications/CanvasSize;)V
@ -1163,6 +1163,16 @@ public final class space/kscience/visionforge/solid/specifications/Canvas3DOptio
public static final fun computeWidth (Lspace/kscience/visionforge/solid/specifications/CanvasSize;Ljava/lang/Number;)I public static final fun computeWidth (Lspace/kscience/visionforge/solid/specifications/CanvasSize;Ljava/lang/Number;)I
} }
public final class space/kscience/visionforge/solid/specifications/Canvas3DUIScheme : space/kscience/dataforge/meta/Scheme {
public static final field Companion Lspace/kscience/visionforge/solid/specifications/Canvas3DUIScheme$Companion;
public fun <init> ()V
public final fun getEnabled ()Z
public final fun setEnabled (Z)V
}
public final class space/kscience/visionforge/solid/specifications/Canvas3DUIScheme$Companion : space/kscience/dataforge/meta/SchemeSpec {
}
public final class space/kscience/visionforge/solid/specifications/CanvasSize : space/kscience/dataforge/meta/Scheme { public final class space/kscience/visionforge/solid/specifications/CanvasSize : space/kscience/dataforge/meta/Scheme {
public static final field Companion Lspace/kscience/visionforge/solid/specifications/CanvasSize$Companion; public static final field Companion Lspace/kscience/visionforge/solid/specifications/CanvasSize$Companion;
public fun <init> ()V public fun <init> ()V
@ -1189,14 +1199,6 @@ public final class space/kscience/visionforge/solid/specifications/Clipping : sp
public fun getDescriptor ()Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor; public fun getDescriptor ()Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;
} }
public final class space/kscience/visionforge/solid/specifications/ControlsScheme : space/kscience/dataforge/meta/Scheme {
public static final field Companion Lspace/kscience/visionforge/solid/specifications/ControlsScheme$Companion;
public fun <init> ()V
}
public final class space/kscience/visionforge/solid/specifications/ControlsScheme$Companion : space/kscience/dataforge/meta/SchemeSpec {
}
public final class space/kscience/visionforge/solid/specifications/PointScheme : space/kscience/dataforge/meta/Scheme { public final class space/kscience/visionforge/solid/specifications/PointScheme : space/kscience/dataforge/meta/Scheme {
public static final field Companion Lspace/kscience/visionforge/solid/specifications/PointScheme$Companion; public static final field Companion Lspace/kscience/visionforge/solid/specifications/PointScheme$Companion;
public fun <init> ()V public fun <init> ()V

View File

@ -62,7 +62,7 @@ public class Canvas3DOptions : Scheme() {
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
public var axes: AxesScheme by spec(AxesScheme) public var axes: AxesScheme by spec(AxesScheme)
public var camera: CameraScheme by spec(CameraScheme) public var camera: CameraScheme by spec(CameraScheme)
public var controls: ControlsScheme by spec(ControlsScheme) public var controls: Canvas3DUIScheme by spec(Canvas3DUIScheme)
public var size: CanvasSize by spec(CanvasSize) public var size: CanvasSize by spec(CanvasSize)
@ -92,7 +92,7 @@ public class Canvas3DOptions : Scheme() {
hide() hide()
} }
scheme(Canvas3DOptions::controls, ControlsScheme) { scheme(Canvas3DOptions::controls, Canvas3DUIScheme) {
hide() hide()
} }

View File

@ -0,0 +1,13 @@
package space.kscience.visionforge.solid.specifications
import space.kscience.dataforge.meta.Scheme
import space.kscience.dataforge.meta.SchemeSpec
import space.kscience.dataforge.meta.boolean
public class Canvas3DUIScheme : Scheme() {
public var enabled: Boolean by boolean{true}
public companion object : SchemeSpec<Canvas3DUIScheme>(::Canvas3DUIScheme)
}

View File

@ -1,9 +0,0 @@
package space.kscience.visionforge.solid.specifications
import space.kscience.dataforge.meta.Scheme
import space.kscience.dataforge.meta.SchemeSpec
public class ControlsScheme : Scheme() {
public companion object : SchemeSpec<ControlsScheme>(::ControlsScheme)
}

View File

@ -4,6 +4,7 @@ import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import space.kscience.dataforge.meta.getValue
import space.kscience.dataforge.meta.int import space.kscience.dataforge.meta.int
import space.kscience.dataforge.meta.set import space.kscience.dataforge.meta.set
import space.kscience.dataforge.meta.string import space.kscience.dataforge.meta.string

View File

@ -9,23 +9,17 @@ kscience {
js { js {
useCommonJs() useCommonJs()
binaries.library() binaries.library()
browser {
commonWebpackConfig {
cssSupport {
enabled.set(true)
} }
}
} useSerialization()
} commonMain {
dependencies {
api(projects.visionforgeCore) api(projects.visionforgeCore)
api("space.kscience:tables-kt:${tablesVersion}") api("space.kscience:tables-kt:${tablesVersion}")
} }
dependencies(jsMain) { jsMain {
implementation(npm("tabulator-tables", "5.5.2")) implementation(npm("tabulator-tables", "5.5.2"))
implementation(npm("@types/tabulator-tables", "5.5.3")) implementation(npm("@types/tabulator-tables", "5.5.3"))
} }
useSerialization()
} }
readme { readme {

View File

@ -13,8 +13,7 @@ import space.kscience.visionforge.VisionPlugin
public class TableVisionPlugin : VisionPlugin() { public class TableVisionPlugin : VisionPlugin() {
override val tag: PluginTag get() = Companion.tag override val tag: PluginTag get() = Companion.tag
override val visionSerializersModule: SerializersModule override val visionSerializersModule: SerializersModule = SerializersModule {
get() = SerializersModule {
polymorphic(Vision::class) { polymorphic(Vision::class) {
subclass(VisionOfTable.serializer()) subclass(VisionOfTable.serializer())
} }

View File

@ -1,22 +1,26 @@
plugins { plugins {
id("space.kscience.gradle.mpp") id("space.kscience.gradle.mpp")
alias(spclibs.plugins.compose)
} }
kotlin{ kotlin {
explicitApi = org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode.Disabled explicitApi = org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode.Disabled
} }
kscience{ kscience {
js{ js {
binaries.library() binaries.library()
} }
jsMain{
dependencies { commonMain {
api(projects.visionforgeSolid) api(projects.visionforgeSolid)
implementation(npm("three", "0.143.0")) api(projects.visionforgeCompose)
implementation(npm("three-csg-ts", "3.1.10"))
implementation(npm("three.meshline","1.4.0"))
} }
jsMain {
implementation(npm("three", "0.143.0"))
implementation(npm("three-csg-ts", "3.1.13"))
implementation(npm("three.meshline", "1.4.0"))
} }
} }

View File

@ -236,7 +236,7 @@ public class ThreeCanvas(
// } // }
// } // }
private fun addControls(element: Node, controls: ControlsScheme) { private fun addControls(element: Node, controls: Canvas3DUIScheme) {
when (controls.meta["type"].string) { when (controls.meta["type"].string) {
"trackball" -> TrackballControls(camera, element) "trackball" -> TrackballControls(camera, element)
else -> OrbitControls(camera, element) else -> OrbitControls(camera, element)

View File

@ -2,14 +2,15 @@ package space.kscience.visionforge.solid.three
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import org.jetbrains.compose.web.renderComposable
import org.w3c.dom.Element import org.w3c.dom.Element
import org.w3c.dom.HTMLElement
import space.kscience.dataforge.context.* import space.kscience.dataforge.context.*
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.names.* import space.kscience.dataforge.names.*
import space.kscience.visionforge.* import space.kscience.visionforge.*
import space.kscience.visionforge.solid.* import space.kscience.visionforge.solid.*
import space.kscience.visionforge.solid.specifications.Canvas3DOptions import space.kscience.visionforge.solid.specifications.Canvas3DOptions
import space.kscience.visionforge.solid.three.compose.ThreeView
import space.kscience.visionforge.solid.three.set import space.kscience.visionforge.solid.three.set
import three.core.Object3D import three.core.Object3D
import kotlin.collections.set import kotlin.collections.set
@ -21,7 +22,7 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
public val solids: Solids by require(Solids) public val solids: Solids by require(Solids)
public val client: VisionClient? get() = context.plugins.get<VisionClient>() public val client: VisionClient by require(JsVisionClient)
private val objectFactories = HashMap<KClass<out Solid>, ThreeFactory<*>>() private val objectFactories = HashMap<KClass<out Solid>, ThreeFactory<*>>()
private val compositeFactory = ThreeCompositeFactory(this) private val compositeFactory = ThreeCompositeFactory(this)
@ -123,15 +124,6 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
} }
} }
private val canvasCache = HashMap<Element, ThreeCanvas>()
public fun getOrCreateCanvas(
element: Element,
options: Canvas3DOptions,
): ThreeCanvas = canvasCache.getOrPut(element) {
ThreeCanvas(this, element, options)
}
override fun content(target: String): Map<Name, Any> { override fun content(target: String): Map<Name, Any> {
return when (target) { return when (target) {
ElementVisionRenderer.TYPE -> mapOf("three".asName() to this) ElementVisionRenderer.TYPE -> mapOf("three".asName() to this)
@ -142,20 +134,11 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
override fun rateVision(vision: Vision): Int = override fun rateVision(vision: Vision): Int =
if (vision is Solid) ElementVisionRenderer.DEFAULT_RATING else ElementVisionRenderer.ZERO_RATING if (vision is Solid) ElementVisionRenderer.DEFAULT_RATING else ElementVisionRenderer.ZERO_RATING
internal fun renderSolid(
element: Element,
vision: Solid,
options: Canvas3DOptions,
): ThreeCanvas = getOrCreateCanvas(element, options).apply {
render(vision)
}
override fun render(element: Element, client: VisionClient, name: Name, vision: Vision, meta: Meta) { override fun render(element: Element, client: VisionClient, name: Name, vision: Vision, meta: Meta) {
renderSolid( require(vision is Solid) { "Expected Solid but found ${vision::class}" }
element, renderComposable(element) {
vision as? Solid ?: error("Solid expected but ${vision::class} found"), ThreeView(solids, vision, null, Canvas3DOptions.read(meta))
Canvas3DOptions.read(meta) }
)
} }
public companion object : PluginFactory<ThreePlugin> { public companion object : PluginFactory<ThreePlugin> {
@ -165,14 +148,6 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
} }
} }
public fun ThreePlugin.render(
element: HTMLElement,
obj: Solid,
optionsBuilder: Canvas3DOptions.() -> Unit = {},
): ThreeCanvas = renderSolid(element, obj, Canvas3DOptions(optionsBuilder)).apply {
options.apply(optionsBuilder)
}
internal operator fun Object3D.set(token: NameToken, object3D: Object3D) { internal operator fun Object3D.set(token: NameToken, object3D: Object3D) {
object3D.name = token.toString() object3D.name = token.toString()
add(object3D) add(object3D)

View File

@ -1,4 +1,4 @@
package space.kscience.visionforge.compose package space.kscience.visionforge.solid.three.compose
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import org.jetbrains.compose.web.css.* import org.jetbrains.compose.web.css.*
@ -9,6 +9,7 @@ import org.w3c.files.BlobPropertyBag
import space.kscience.dataforge.context.Global import space.kscience.dataforge.context.Global
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.visionforge.Vision import space.kscience.visionforge.Vision
import space.kscience.visionforge.compose.*
import space.kscience.visionforge.encodeToString import space.kscience.visionforge.encodeToString
import space.kscience.visionforge.solid.specifications.Canvas3DOptions import space.kscience.visionforge.solid.specifications.Canvas3DOptions

View File

@ -0,0 +1,205 @@
package space.kscience.visionforge.solid.three.compose
import androidx.compose.runtime.*
import app.softwork.bootstrapcompose.Card
import kotlinx.dom.clear
import org.jetbrains.compose.web.ExperimentalComposeWebApi
import org.jetbrains.compose.web.css.*
import org.jetbrains.compose.web.dom.*
import space.kscience.dataforge.context.Context
import space.kscience.dataforge.context.request
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.isEmpty
import space.kscience.visionforge.Vision
import space.kscience.visionforge.compose.*
import space.kscience.visionforge.root
import space.kscience.visionforge.solid.Solid
import space.kscience.visionforge.solid.SolidGroup
import space.kscience.visionforge.solid.Solids
import space.kscience.visionforge.solid.specifications.Canvas3DOptions
import space.kscience.visionforge.solid.three.ThreeCanvas
import space.kscience.visionforge.solid.three.ThreePlugin
import space.kscience.visionforge.styles
@Composable
private fun SimpleThreeView(
context: Context,
options: Canvas3DOptions?,
solid: Solid?,
selected: Name?,
) {
val three: ThreePlugin by derivedStateOf { context.request(ThreePlugin) }
Div({
style {
maxWidth(100.vw)
maxHeight(100.vh)
width(100.percent)
height(100.percent)
}
}) {
var canvas: ThreeCanvas? = null
DisposableEffect(options) {
canvas = ThreeCanvas(three, scopeElement, options ?: Canvas3DOptions())
onDispose {
scopeElement.clear()
canvas = null
}
}
LaunchedEffect(solid) {
if (solid != null) {
canvas?.render(solid)
} else {
canvas?.clear()
}
}
LaunchedEffect(selected) {
canvas?.select(selected)
}
}
}
@Composable
public fun ThreeView(
solids: Solids,
solid: Solid?,
initialSelected: Name? = null,
options: Canvas3DOptions? = null,
sidebarTabs: @Composable TabsBuilder.() -> Unit = {},
) {
var selected: Name? by remember { mutableStateOf(initialSelected) }
val optionsSnapshot = remember(options) {
(options ?: Canvas3DOptions()).apply {
this.onSelect = {
selected = it
}
}
}
val selectedVision: Vision? = remember(solid, selected) {
selected?.let {
when {
it.isEmpty() -> solid
else -> (solid as? SolidGroup)?.get(it)
}
}
}
if (optionsSnapshot.controls.enabled) {
FlexRow({
style {
height(100.percent)
width(100.percent)
flexWrap(FlexWrap.Wrap)
alignItems(AlignItems.Stretch)
alignContent(AlignContent.Stretch)
}
}) {
FlexColumn({
style {
height(100.percent)
minWidth(600.px)
flex(10, 1, 600.px)
position(Position.Relative)
}
}) {
if (solid == null) {
Div({
style {
position(Position.Fixed)
width(100.percent)
height(100.percent)
zIndex(1000)
top(40.percent)
left(0.px)
opacity(0.5)
@OptIn(ExperimentalComposeWebApi::class) filter {
opacity(50.percent)
}
}
}) {
Div({ classes("d-flex", " justify-content-center") }) {
Div({
classes("spinner-grow", "text-primary")
style {
width(3.cssRem)
height(3.cssRem)
zIndex(20)
}
attr("role", "status")
}) {
Span({ classes("sr-only") }) { Text("Loading 3D vision") }
}
}
}
} else {
SimpleThreeView(solids.context, optionsSnapshot, solid, selected)
}
selectedVision?.let { vision ->
Div({
style {
position(Position.Absolute)
top(5.px)
right(5.px)
width(450.px)
}
}) {
Card(
headerAttrs = {
// border = true
},
header = {
NameCrumbs(selected) { selected = it }
}
) {
PropertyEditor(
scope = solids.context,
meta = vision.properties.root(),
getPropertyState = { name ->
if (vision.properties.own?.get(name) != null) {
EditorPropertyState.Defined
} else if (vision.properties.root()[name] != null) {
// TODO differentiate
EditorPropertyState.Default()
} else {
EditorPropertyState.Undefined
}
},
updates = vision.properties.changes,
rootDescriptor = vision.descriptor
)
}
vision.styles.takeIf { it.isNotEmpty() }?.let { styles ->
P {
B { Text("Styles: ") }
Text(styles.joinToString(separator = ", "))
}
}
}
}
}
}
FlexColumn({
style {
paddingAll(4.px)
minWidth(400.px)
height(100.percent)
overflowY("auto")
flex(1, 10, 300.px)
}
}) {
ThreeControls(solid, optionsSnapshot, selected, onSelect = { selected = it }, tabBuilder = sidebarTabs)
}
} else {
SimpleThreeView(solids.context, optionsSnapshot, solid, selected)
}
}

View File

@ -1,29 +1,24 @@
plugins { plugins {
id("space.kscience.gradle.mpp") id("space.kscience.gradle.mpp")
alias(spclibs.plugins.compose)
} }
val ktorVersion: String by rootProject.extra val ktorVersion: String by rootProject.extra
kscience { kscience {
fullStack("js/visionforge-three.js") { fullStack("js/visionforge-three.js")
commonWebpackConfig {
cssSupport {
enabled.set(false)
}
}
}
dependencies { commonMain {
api(projects.visionforgeSolid) api(projects.visionforgeSolid)
api(projects.visionforgeCompose)
} }
dependencies(jvmMain) { jvmMain{
api(projects.visionforgeServer) api(projects.visionforgeServer)
} }
dependencies(jsMain) { jsMain{
api(projects.visionforgeThreejs) api(projects.visionforgeThreejs)
api(projects.ui.ring)
compileOnly(npm("webpack-bundle-analyzer","4.5.0")) compileOnly(npm("webpack-bundle-analyzer","4.5.0"))
} }
} }

View File

@ -1,11 +1,11 @@
package space.kscience.visionforge.three package space.kscience.visionforge.three
import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.misc.DFExperimental
import space.kscience.visionforge.ring.ThreeWithControlsPlugin
import space.kscience.visionforge.runVisionClient import space.kscience.visionforge.runVisionClient
import space.kscience.visionforge.solid.three.ThreePlugin
@DFExperimental @DFExperimental
public fun main(): Unit = runVisionClient { public fun main(): Unit = runVisionClient {
plugin(ThreeWithControlsPlugin) plugin(ThreePlugin)
} }

View File

@ -1,23 +0,0 @@
const ringConfig = require('@jetbrains/ring-ui/webpack.config').config;
const path = require('path');
config.module.rules.push(...ringConfig.module.rules)
config.module.rules.push(
{
test: /\.css$/,
exclude: [
path.resolve(__dirname, "../../node_modules/@jetbrains/ring-ui")
],
use: [
{
loader: 'style-loader',
options: {}
},
{
loader: 'css-loader',
options: {}
}
]
}
)

View File

@ -1,10 +0,0 @@
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
plugins: [
new BundleAnalyzerPlugin({
analyzerMode: "static",
reportFilename: "bundle-report.html"
})
]
}