Moving to compose
This commit is contained in:
parent
0c9d849e97
commit
4f6f4b9268
@ -5,6 +5,7 @@
|
||||
- Context receivers flag
|
||||
- MeshLine for thick lines
|
||||
- Custom client-side events and thier processing in VisionServer
|
||||
- Control/input visions
|
||||
|
||||
### Changed
|
||||
- Color accessor property is now `colorProperty`. Color uses non-nullable `invoke` instead of `set`.
|
||||
|
@ -1,4 +1,3 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinJsCompile
|
||||
import space.kscience.gradle.useApache2Licence
|
||||
import space.kscience.gradle.useSPCTeam
|
||||
|
||||
@ -11,7 +10,7 @@ val dataforgeVersion by extra("0.7.1")
|
||||
|
||||
allprojects {
|
||||
group = "space.kscience"
|
||||
version = "0.3.0-RC"
|
||||
version = "0.4.0-dev-1"
|
||||
}
|
||||
|
||||
subprojects {
|
||||
@ -31,11 +30,11 @@ subprojects {
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<KotlinJsCompile>{
|
||||
kotlinOptions{
|
||||
useEsClasses = true
|
||||
}
|
||||
}
|
||||
// tasks.withType<KotlinJsCompile>{
|
||||
// kotlinOptions{
|
||||
// useEsClasses = true
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
ksciencePublish {
|
||||
|
@ -7,14 +7,8 @@ group = "demo"
|
||||
kscience {
|
||||
jvm()
|
||||
js {
|
||||
useCommonJs()
|
||||
browser {
|
||||
binaries.executable()
|
||||
commonWebpackConfig {
|
||||
cssSupport {
|
||||
enabled.set(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
@ -26,7 +20,6 @@ kscience {
|
||||
implementation(spclibs.logback.classic)
|
||||
}
|
||||
jsMain {
|
||||
implementation(projects.ui.ring)
|
||||
implementation(projects.visionforgeThreejs)
|
||||
implementation(npm("react-file-drop", "3.0.6"))
|
||||
}
|
||||
|
@ -1,46 +1,33 @@
|
||||
package space.kscience.visionforge.gdml.demo
|
||||
|
||||
import androidx.compose.runtime.*
|
||||
import kotlinx.browser.window
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.css.*
|
||||
import org.jetbrains.compose.web.css.*
|
||||
import org.jetbrains.compose.web.dom.Div
|
||||
import org.jetbrains.compose.web.dom.H2
|
||||
import org.jetbrains.compose.web.dom.Text
|
||||
import org.w3c.files.File
|
||||
import org.w3c.files.FileReader
|
||||
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.gdml.Gdml
|
||||
import space.kscience.gdml.decodeFromString
|
||||
import space.kscience.visionforge.Colors
|
||||
import space.kscience.visionforge.gdml.markLayers
|
||||
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.solid.Solid
|
||||
import space.kscience.visionforge.solid.Solids
|
||||
import space.kscience.visionforge.solid.ambientLight
|
||||
import space.kscience.visionforge.solid.invoke
|
||||
import styled.css
|
||||
import styled.styledDiv
|
||||
import space.kscience.visionforge.solid.three.compose.ThreeView
|
||||
|
||||
external interface GDMLAppProps : Props {
|
||||
var solids: Solids
|
||||
var vision: Solid?
|
||||
var selected: Name?
|
||||
}
|
||||
|
||||
@JsExport
|
||||
val GDMLApp = fc<GDMLAppProps>("GDMLApp") { props ->
|
||||
var deferredVision: Deferred<Solid?> by useState {
|
||||
CompletableDeferred(props.vision)
|
||||
}
|
||||
@Composable
|
||||
fun GDMLApp(solids: Solids, initialVision: Solid?, selected: Name? = null) {
|
||||
var vision: Solid? by remember { mutableStateOf(initialVision) }
|
||||
|
||||
fun readFileAsync(file: File): Deferred<Solid?> {
|
||||
val deferred = CompletableDeferred<Solid?>()
|
||||
fun readFileAsync(file: File) {
|
||||
FileReader().apply {
|
||||
onload = {
|
||||
val data = result as String
|
||||
@ -49,7 +36,7 @@ val GDMLApp = fc<GDMLAppProps>("GDMLApp") { props ->
|
||||
name.endsWith(".gdml") || name.endsWith(".xml") -> {
|
||||
val gdml = Gdml.decodeFromString(data)
|
||||
gdml.toVision().apply {
|
||||
setAsRoot(props.solids.visionManager)
|
||||
setAsRoot(solids.visionManager)
|
||||
console.info("Marking layers for file $name")
|
||||
markLayers()
|
||||
ambientLight {
|
||||
@ -57,43 +44,38 @@ val GDMLApp = fc<GDMLAppProps>("GDMLApp") { props ->
|
||||
}
|
||||
}
|
||||
}
|
||||
name.endsWith(".json") -> props.solids.visionManager.decodeFromString(data)
|
||||
|
||||
name.endsWith(".json") -> solids.visionManager.decodeFromString(data)
|
||||
else -> {
|
||||
window.alert("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)
|
||||
}
|
||||
|
||||
return deferred
|
||||
}
|
||||
|
||||
styledDiv {
|
||||
css {
|
||||
height = 100.vh - 12.pt
|
||||
width = 100.vw
|
||||
Div({
|
||||
style {
|
||||
height(100.vh - 12.pt)
|
||||
width(100.vw)
|
||||
}
|
||||
child(ThreeCanvasWithControls) {
|
||||
attrs {
|
||||
this.solids = props.solids
|
||||
this.builderOfSolid = deferredVision
|
||||
this.selected = props.selected
|
||||
tab("Load") {
|
||||
h2 {
|
||||
+"Drag and drop .gdml or .json VisionForge files here"
|
||||
}
|
||||
fileDrop("(drag file here)") { files ->
|
||||
val file = files?.get(0)
|
||||
if (file != null) {
|
||||
deferredVision = readFileAsync(file)
|
||||
}
|
||||
}) {
|
||||
ThreeView(solids, vision, selected) {
|
||||
Tab("Load") {
|
||||
H2 {
|
||||
Text("Drag and drop .gdml or .json VisionForge files here")
|
||||
}
|
||||
fileDrop("(drag file here)") { files ->
|
||||
val file = files?.get(0)
|
||||
if (file != null) {
|
||||
readFileAsync(file)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,63 +1,58 @@
|
||||
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 space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.context.request
|
||||
import space.kscience.gdml.GdmlShowCase
|
||||
import space.kscience.visionforge.Application
|
||||
import space.kscience.visionforge.Colors
|
||||
import space.kscience.visionforge.compose.TreeStyles
|
||||
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.ambientLight
|
||||
import space.kscience.visionforge.solid.invoke
|
||||
import space.kscience.visionforge.solid.three.ThreePlugin
|
||||
import space.kscience.visionforge.startApplication
|
||||
import styled.injectGlobal
|
||||
|
||||
|
||||
private class GDMLDemoApp : Application {
|
||||
|
||||
override fun start(document: Document, state: Map<String, Any>) {
|
||||
val context = Context("gdml-demo"){
|
||||
val context = Context("gdml-demo") {
|
||||
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")
|
||||
|
||||
createRoot(element).render {
|
||||
child(GDMLApp) {
|
||||
val vision = GdmlShowCase.cubes().toVision().apply {
|
||||
ambientLight {
|
||||
color(Colors.white)
|
||||
}
|
||||
val vision = GdmlShowCase.cubes().toVision().apply {
|
||||
ambientLight {
|
||||
color(Colors.white)
|
||||
}
|
||||
}
|
||||
|
||||
renderComposable(element) {
|
||||
Style(TreeStyles)
|
||||
Style {
|
||||
"html" {
|
||||
height(100.percent)
|
||||
}
|
||||
//println(context.plugins.fetch(VisionManager).encodeToString(vision))
|
||||
attrs {
|
||||
this.solids = context.request(Solids)
|
||||
this.vision = vision
|
||||
|
||||
"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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,14 +9,8 @@ kscience {
|
||||
kotlin {
|
||||
explicitApi = null
|
||||
js {
|
||||
useCommonJs()
|
||||
browser {
|
||||
binaries.executable()
|
||||
commonWebpackConfig {
|
||||
cssSupport {
|
||||
enabled.set(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -29,7 +23,4 @@ kscience {
|
||||
implementation(projects.visionforge.visionforgeMarkdown)
|
||||
implementation(projects.visionforge.visionforgeThreejs)
|
||||
}
|
||||
jsMain {
|
||||
implementation(projects.ui.ring)
|
||||
}
|
||||
}
|
@ -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 ringui.SmartTabs
|
||||
import ringui.Tab
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.context.request
|
||||
import space.kscience.plotly.models.Trace
|
||||
@ -9,16 +9,12 @@ import space.kscience.plotly.scatter
|
||||
import space.kscience.visionforge.Application
|
||||
import space.kscience.visionforge.Colors
|
||||
import space.kscience.visionforge.JsVisionClient
|
||||
import space.kscience.visionforge.compose.Tabs
|
||||
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.three.ThreePlugin
|
||||
import space.kscience.visionforge.solid.three.compose.ThreeView
|
||||
import space.kscience.visionforge.startApplication
|
||||
import styled.css
|
||||
import styled.styledDiv
|
||||
import kotlin.random.Random
|
||||
|
||||
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>) {
|
||||
|
||||
val playgroundContext = Context {
|
||||
plugin(ThreeWithControlsPlugin)
|
||||
plugin(JsVisionClient)
|
||||
plugin(ThreePlugin)
|
||||
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")
|
||||
|
||||
createRoot(element).render {
|
||||
styledDiv {
|
||||
css {
|
||||
padding = Padding(0.pt)
|
||||
margin = Margin(0.pt)
|
||||
height = 100.vh
|
||||
width = 100.vw
|
||||
renderComposable(element) {
|
||||
Div({
|
||||
style {
|
||||
padding(0.pt)
|
||||
margin(0.pt)
|
||||
height(100.vh)
|
||||
width(100.vw)
|
||||
}
|
||||
SmartTabs("gravity") {
|
||||
}) {
|
||||
Tabs {
|
||||
active = "gravity"
|
||||
Tab("gravity") {
|
||||
GravityDemo {
|
||||
attrs {
|
||||
this.solids = playgroundContext.request(Solids)
|
||||
}
|
||||
}
|
||||
GravityDemo(solids, client)
|
||||
}
|
||||
|
||||
// Tab("D0") {
|
||||
@ -66,43 +62,34 @@ private class JsPlaygroundApp : Application {
|
||||
// }
|
||||
// }
|
||||
Tab("spheres") {
|
||||
styledDiv {
|
||||
css {
|
||||
height = 100.vh - 50.pt
|
||||
Div({
|
||||
style {
|
||||
height(100.vh - 50.pt)
|
||||
}
|
||||
child(ThreeCanvasWithControls) {
|
||||
val random = Random(112233)
|
||||
attrs {
|
||||
solids = playgroundContext.request(Solids)
|
||||
solid {
|
||||
ambientLight {
|
||||
color(Colors.white)
|
||||
}
|
||||
repeat(100) {
|
||||
sphere(5, name = "sphere[$it]") {
|
||||
x = random.nextDouble(-300.0, 300.0)
|
||||
y = random.nextDouble(-300.0, 300.0)
|
||||
z = random.nextDouble(-300.0, 300.0)
|
||||
material {
|
||||
color(random.nextInt())
|
||||
}
|
||||
detail = 16
|
||||
}
|
||||
}) {
|
||||
ThreeView(solids, SolidGroup {
|
||||
ambientLight {
|
||||
color(Colors.white)
|
||||
}
|
||||
repeat(100) {
|
||||
sphere(5, name = "sphere[$it]") {
|
||||
x = Random.nextDouble(-300.0, 300.0)
|
||||
y = Random.nextDouble(-300.0, 300.0)
|
||||
z = Random.nextDouble(-300.0, 300.0)
|
||||
material {
|
||||
color(Random.nextInt())
|
||||
}
|
||||
detail = 16
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Tab("plotly") {
|
||||
Plotly {
|
||||
attrs {
|
||||
plot = space.kscience.plotly.Plotly.plot {
|
||||
scatter {
|
||||
x(1, 2, 3)
|
||||
y(5, 8, 7)
|
||||
}
|
||||
}
|
||||
Plot(client) {
|
||||
scatter {
|
||||
x(1, 2, 3)
|
||||
y(5, 8, 7)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -110,6 +97,7 @@ private class JsPlaygroundApp : Application {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public fun main() {
|
||||
|
@ -1,111 +1,159 @@
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.remember
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.css.*
|
||||
import react.Props
|
||||
import react.fc
|
||||
import org.jetbrains.compose.web.css.*
|
||||
import org.jetbrains.compose.web.dom.AttrBuilderContext
|
||||
import org.jetbrains.compose.web.dom.Div
|
||||
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.models.Trace
|
||||
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.react.flexRow
|
||||
import space.kscience.visionforge.ring.ThreeCanvasWithControls
|
||||
import space.kscience.visionforge.ring.solid
|
||||
import space.kscience.visionforge.plotly.asVision
|
||||
import space.kscience.visionforge.solid.*
|
||||
import styled.css
|
||||
import styled.styledDiv
|
||||
import space.kscience.visionforge.solid.three.compose.ThreeView
|
||||
import kotlin.math.sqrt
|
||||
|
||||
external interface DemoProps : Props {
|
||||
var solids: Solids
|
||||
}
|
||||
@Composable
|
||||
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 ->
|
||||
val velocityTrace = Trace {
|
||||
name = "velocity"
|
||||
}
|
||||
val energyTrace = Trace {
|
||||
name = "energy"
|
||||
}
|
||||
val markup = VisionOfMarkup()
|
||||
@Composable
|
||||
fun Markup(
|
||||
client: VisionClient,
|
||||
markup: VisionOfMarkup,
|
||||
meta: Meta = Meta.EMPTY,
|
||||
attrs: AttrBuilderContext<HTMLDivElement>? = null,
|
||||
) = Vision(
|
||||
client = client,
|
||||
attrs = attrs,
|
||||
meta = meta,
|
||||
vision = markup
|
||||
)
|
||||
|
||||
styledDiv {
|
||||
css {
|
||||
height = 100.vh - 50.pt
|
||||
|
||||
private val h = 100.0
|
||||
|
||||
@Composable
|
||||
fun GravityDemo(solids: Solids, client: VisionClient) {
|
||||
val velocityTrace = remember {
|
||||
Trace {
|
||||
name = "velocity"
|
||||
}
|
||||
styledDiv {
|
||||
css {
|
||||
height = 50.vh
|
||||
}
|
||||
|
||||
val energyTrace = remember {
|
||||
Trace {
|
||||
name = "energy"
|
||||
}
|
||||
}
|
||||
|
||||
val markup = remember { VisionOfMarkup() }
|
||||
|
||||
val solid = remember {
|
||||
SolidGroup {
|
||||
pointLight(200, 200, 200, name = "light") {
|
||||
color(Colors.white)
|
||||
}
|
||||
child(ThreeCanvasWithControls) {
|
||||
attrs {
|
||||
solids = props.solids
|
||||
solid {
|
||||
pointLight(200, 200, 200, name = "light"){
|
||||
color(Colors.white)
|
||||
}
|
||||
ambientLight()
|
||||
ambientLight()
|
||||
|
||||
sphere(5.0, "ball") {
|
||||
detail = 16
|
||||
color("red")
|
||||
val h = 100.0
|
||||
y = h
|
||||
solids.context.launch {
|
||||
val g = 10.0
|
||||
val dt = 0.1
|
||||
var time = 0.0
|
||||
var velocity = 0.0
|
||||
while (isActive) {
|
||||
delay(20)
|
||||
time += dt
|
||||
velocity -= g * dt
|
||||
val energy = g * y.toDouble() + velocity * velocity / 2
|
||||
y = y.toDouble() + velocity * dt
|
||||
sphere(5.0, "ball") {
|
||||
detail = 16
|
||||
color("red")
|
||||
y = h
|
||||
|
||||
velocityTrace.appendXYLatest(time, y)
|
||||
energyTrace.appendXYLatest(time, energy)
|
||||
|
||||
if (y.toDouble() <= 2.5) {
|
||||
//conservation of energy
|
||||
velocity = sqrt(2 * g * h)
|
||||
}
|
||||
|
||||
markup.content = """
|
||||
## Bouncing sphere parameters
|
||||
|
||||
**velocity** = $velocity
|
||||
|
||||
**energy** = $energy
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
box(200, 5, 200, name = "floor") {
|
||||
y = -2.5
|
||||
}
|
||||
}
|
||||
box(200, 5, 200, name = "floor") {
|
||||
y = -2.5
|
||||
}
|
||||
}
|
||||
}
|
||||
flexRow {
|
||||
css {
|
||||
alignContent = Align.stretch
|
||||
alignItems = Align.stretch
|
||||
height = 50.vh - 50.pt
|
||||
}
|
||||
|
||||
LaunchedEffect(solid) {
|
||||
val ball = solid["ball"]!!
|
||||
val g = 10.0
|
||||
val dt = 0.1
|
||||
var time = 0.0
|
||||
var velocity = 0.0
|
||||
while (isActive) {
|
||||
delay(20)
|
||||
time += dt
|
||||
velocity -= g * dt
|
||||
val energy = g * ball.y.toDouble() + velocity * velocity / 2
|
||||
ball.y = ball.y.toDouble() + velocity * dt
|
||||
|
||||
velocityTrace.appendXYLatest(time, ball.y)
|
||||
energyTrace.appendXYLatest(time, energy)
|
||||
|
||||
if (ball.y.toDouble() <= 2.5) {
|
||||
//conservation of energy
|
||||
velocity = sqrt(2 * g * h)
|
||||
}
|
||||
plotly {
|
||||
|
||||
markup.content = """
|
||||
## Bouncing sphere parameters
|
||||
|
||||
**velocity** = $velocity
|
||||
|
||||
**energy** = $energy
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
Div({
|
||||
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)
|
||||
}
|
||||
}) {
|
||||
Plot(client) {
|
||||
traces(velocityTrace, energyTrace)
|
||||
layout {
|
||||
xaxis.title = "time"
|
||||
}
|
||||
}
|
||||
Markup {
|
||||
attrs {
|
||||
this.markup = markup
|
||||
Markup(client, markup, attrs = {
|
||||
style {
|
||||
width(100.percent)
|
||||
height(100.percent)
|
||||
border(2.pt, LineStyle.Solid, Color.blue)
|
||||
paddingLeft(8.pt)
|
||||
backgroundColor(Color.white)
|
||||
flex(1)
|
||||
zIndex(10000)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -15,13 +15,7 @@ kscience {
|
||||
"muon-monitor.js",
|
||||
jvmConfig = { withJava() },
|
||||
jsConfig = { useCommonJs() }
|
||||
) {
|
||||
commonWebpackConfig {
|
||||
cssSupport {
|
||||
enabled.set(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
commonMain {
|
||||
implementation(projects.visionforgeSolid)
|
||||
@ -34,7 +28,6 @@ kscience {
|
||||
implementation("ch.qos.logback:logback-classic:1.2.11")
|
||||
}
|
||||
jsMain {
|
||||
implementation(projects.ui.ring)
|
||||
implementation(projects.visionforgeThreejs)
|
||||
//implementation(devNpm("webpack-bundle-analyzer", "4.4.0"))
|
||||
}
|
||||
|
@ -1,281 +1,106 @@
|
||||
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.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.await
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.css.*
|
||||
import kotlinx.html.js.onClickFunction
|
||||
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 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.names.Name
|
||||
import space.kscience.visionforge.Colors
|
||||
import space.kscience.visionforge.react.flexColumn
|
||||
import space.kscience.visionforge.react.flexRow
|
||||
import space.kscience.visionforge.ring.ThreeCanvasWithControls
|
||||
import space.kscience.visionforge.ring.tab
|
||||
import space.kscience.visionforge.compose.FlexColumn
|
||||
import space.kscience.visionforge.compose.FlexRow
|
||||
import space.kscience.visionforge.solid.Solids
|
||||
import space.kscience.visionforge.solid.ambientLight
|
||||
import space.kscience.visionforge.solid.edges
|
||||
import space.kscience.visionforge.solid.invoke
|
||||
import space.kscience.visionforge.solid.specifications.Canvas3DOptions
|
||||
import styled.css
|
||||
import styled.styledDiv
|
||||
import styled.styledSpan
|
||||
import space.kscience.visionforge.solid.three.compose.ThreeView
|
||||
import kotlin.math.PI
|
||||
|
||||
external interface MMAppProps : Props {
|
||||
var model: Model
|
||||
var solids: Solids
|
||||
var selected: Name?
|
||||
}
|
||||
@Composable
|
||||
fun MMApp(solids: Solids, model: Model, selected: Name? = null) {
|
||||
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
@JsExport
|
||||
val MMApp = fc<MMAppProps>("Muon monitor") { props ->
|
||||
|
||||
val mmOptions = useMemo {
|
||||
val mmOptions = remember {
|
||||
Canvas3DOptions {
|
||||
camera {
|
||||
distance = 2100.0
|
||||
latitude = PI / 6
|
||||
azimuth = PI + PI / 6
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
val root = useMemo(props.model) {
|
||||
props.model.root.apply {
|
||||
val root = remember(model) {
|
||||
model.root.apply {
|
||||
edges()
|
||||
ambientLight{
|
||||
ambientLight {
|
||||
color(Colors.white)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var events: Set<Event> by useState(emptySet())
|
||||
val events = remember { mutableStateListOf<Event>() }
|
||||
|
||||
styledDiv {
|
||||
css {
|
||||
height = 100.vh - 12.pt
|
||||
Div({
|
||||
style {
|
||||
height(100.vh - 12.pt)
|
||||
}
|
||||
child(ThreeCanvasWithControls) {
|
||||
attrs {
|
||||
this.solids = props.solids
|
||||
this.builderOfSolid = CompletableDeferred(root)
|
||||
this.selected = props.selected
|
||||
this.options = mmOptions
|
||||
tab("Events") {
|
||||
flexColumn {
|
||||
flexRow {
|
||||
button {
|
||||
+"Next"
|
||||
attrs {
|
||||
onClickFunction = {
|
||||
solids.context.launch {
|
||||
val event = window.fetch(
|
||||
"http://localhost:8080/event",
|
||||
RequestInit("GET")
|
||||
).then { response ->
|
||||
if (response.ok) {
|
||||
response.text()
|
||||
} else {
|
||||
error("Failed to get event")
|
||||
}
|
||||
}.then { body ->
|
||||
Json.decodeFromString(Event.serializer(), body)
|
||||
}.await()
|
||||
events = events + event
|
||||
props.model.displayEvent(event)
|
||||
}
|
||||
}) {
|
||||
ThreeView(solids, root, selected, mmOptions) {
|
||||
Tab("Events") {
|
||||
|
||||
FlexColumn {
|
||||
FlexRow {
|
||||
Button("Next") {
|
||||
solids.context.launch {
|
||||
val event = window.fetch(
|
||||
"http://localhost:8080/event",
|
||||
RequestInit("GET")
|
||||
).then { response ->
|
||||
if (response.ok) {
|
||||
response.text()
|
||||
} else {
|
||||
error("Failed to get event")
|
||||
}
|
||||
}
|
||||
}
|
||||
button {
|
||||
+"Clear"
|
||||
attrs {
|
||||
onClickFunction = {
|
||||
events = emptySet()
|
||||
props.model.reset()
|
||||
}
|
||||
}
|
||||
}.then { body ->
|
||||
Json.decodeFromString(Event.serializer(), body)
|
||||
}.await()
|
||||
events.add(event)
|
||||
model.displayEvent(event)
|
||||
}
|
||||
}
|
||||
Button("Clear") {
|
||||
events.clear()
|
||||
model.reset()
|
||||
}
|
||||
}
|
||||
events.forEach { event ->
|
||||
p {
|
||||
styledSpan {
|
||||
+event.id.toString()
|
||||
}
|
||||
+" : "
|
||||
styledSpan {
|
||||
css {
|
||||
color = Color.blue
|
||||
}
|
||||
+event.hits.toString()
|
||||
}
|
||||
events.forEach { event ->
|
||||
P {
|
||||
Span {
|
||||
Text(event.id.toString())
|
||||
}
|
||||
Text(" : ")
|
||||
Span({
|
||||
style {
|
||||
color(Color.blue)
|
||||
}
|
||||
}) {
|
||||
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)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// }
|
||||
}
|
@ -1,12 +1,11 @@
|
||||
package ru.mipt.npm.muon.monitor
|
||||
|
||||
import org.jetbrains.compose.web.renderComposable
|
||||
import org.w3c.dom.Document
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.context.request
|
||||
import space.kscience.visionforge.Application
|
||||
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.three.ThreePlugin
|
||||
import space.kscience.visionforge.startApplication
|
||||
@ -24,13 +23,8 @@ private class MMDemoApp : Application {
|
||||
val model = Model(visionManager)
|
||||
|
||||
val element = document.getElementById("app") ?: error("Element with id 'app' not found on page")
|
||||
createRoot(element).render {
|
||||
child(MMApp) {
|
||||
attrs {
|
||||
this.model = model
|
||||
this.solids = context.request(Solids)
|
||||
}
|
||||
}
|
||||
renderComposable(element) {
|
||||
MMApp(context.request(Solids), model)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,17 +14,10 @@ repositories {
|
||||
kotlin {
|
||||
|
||||
js(IR) {
|
||||
useCommonJs()
|
||||
browser {
|
||||
webpackTask {
|
||||
mainOutputFileName.set("js/visionforge-playground.js")
|
||||
}
|
||||
commonWebpackConfig {
|
||||
sourceMaps = true
|
||||
cssSupport{
|
||||
enabled.set(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
binaries.executable()
|
||||
}
|
||||
@ -57,7 +50,6 @@ kotlin {
|
||||
|
||||
val jsMain by getting {
|
||||
dependencies {
|
||||
implementation(projects.ui.ring)
|
||||
implementation(projects.visionforgeThreejs)
|
||||
compileOnly(npm("webpack-bundle-analyzer","4.5.0"))
|
||||
}
|
||||
|
@ -2,13 +2,13 @@ import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.visionforge.jupyter.VFNotebookClient
|
||||
import space.kscience.visionforge.markup.MarkupPlugin
|
||||
import space.kscience.visionforge.plotly.PlotlyPlugin
|
||||
import space.kscience.visionforge.ring.ThreeWithControlsPlugin
|
||||
import space.kscience.visionforge.runVisionClient
|
||||
import space.kscience.visionforge.solid.three.ThreePlugin
|
||||
import space.kscience.visionforge.tables.TableVisionJsPlugin
|
||||
|
||||
@DFExperimental
|
||||
fun main() = runVisionClient {
|
||||
plugin(ThreeWithControlsPlugin)
|
||||
plugin(ThreePlugin)
|
||||
plugin(PlotlyPlugin)
|
||||
plugin(MarkupPlugin)
|
||||
plugin(TableVisionJsPlugin)
|
||||
|
@ -1,3 +1,6 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode
|
||||
|
||||
|
||||
plugins {
|
||||
id("space.kscience.gradle.mpp")
|
||||
alias(spclibs.plugins.ktor)
|
||||
@ -19,6 +22,8 @@ kscience {
|
||||
|
||||
group = "center.sciprog"
|
||||
|
||||
kotlin.explicitApi = ExplicitApiMode.Disabled
|
||||
|
||||
application {
|
||||
mainClass.set("ru.mipt.npm.sat.SatServerKt")
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ class ThreeDemoGrid(element: Element) : VisionLayout<Solid> {
|
||||
}
|
||||
}
|
||||
val element = document.getElementById("output-$name") ?: error("Element not found")
|
||||
three.getOrCreateCanvas(element, canvasOptions)
|
||||
ThreeCanvas(three, element, canvasOptions)
|
||||
}.render(vision)
|
||||
}
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ include(
|
||||
":ui:ring",
|
||||
// ":ui:material",
|
||||
":ui:bootstrap",
|
||||
":ui:compose",
|
||||
":visionforge-compose",
|
||||
":visionforge-core",
|
||||
":visionforge-solid",
|
||||
// ":visionforge-fx",
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,8 +7,6 @@ import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.context.PluginFactory
|
||||
import space.kscience.dataforge.context.PluginTag
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.boolean
|
||||
import space.kscience.dataforge.meta.get
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.asName
|
||||
import space.kscience.visionforge.ElementVisionRenderer
|
||||
@ -28,16 +26,12 @@ public class ThreeWithControlsPlugin : AbstractPlugin(), ElementVisionRenderer {
|
||||
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) {
|
||||
if (meta["controls.enabled"].boolean == false) {
|
||||
three.render(element, client, name, vision, meta)
|
||||
} else {
|
||||
space.kscience.visionforge.react.createRoot(element).render {
|
||||
child(ThreeCanvasWithControls) {
|
||||
attrs {
|
||||
this.solids = three.solids
|
||||
this.options = Canvas3DOptions.read(meta)
|
||||
this.builderOfSolid = context.async { vision as Solid }
|
||||
}
|
||||
space.kscience.visionforge.react.createRoot(element).render {
|
||||
child(ThreeCanvasWithControls) {
|
||||
attrs {
|
||||
this.solids = three.solids
|
||||
this.options = Canvas3DOptions.read(meta)
|
||||
this.builderOfSolid = context.async { vision as Solid }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,6 @@
|
||||
plugins {
|
||||
id("space.kscience.gradle.mpp")
|
||||
alias(spclibs.plugins.compose)
|
||||
// id("org.jetbrains.compose") version "1.5.11"
|
||||
// id("com.android.library")
|
||||
}
|
||||
|
||||
kscience{
|
||||
@ -15,9 +13,9 @@ kscience{
|
||||
kotlin {
|
||||
// android()
|
||||
sourceSets {
|
||||
val commonMain by getting {
|
||||
dependencies {
|
||||
|
||||
commonMain{
|
||||
dependencies{
|
||||
api(projects.visionforgeCore)
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,7 +33,6 @@ kotlin {
|
||||
api(compose.html.core)
|
||||
api("app.softwork:bootstrap-compose:0.1.15")
|
||||
api("app.softwork:bootstrap-compose-icons:0.1.15")
|
||||
api(projects.visionforge.visionforgeThreejs)
|
||||
}
|
||||
}
|
||||
}
|
@ -10,7 +10,6 @@ import org.jetbrains.compose.web.dom.Text
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||
import space.kscience.dataforge.meta.descriptors.get
|
||||
import space.kscience.dataforge.meta.get
|
||||
import space.kscience.dataforge.meta.isLeaf
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.NameToken
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
@ -20,7 +20,6 @@ import space.kscience.dataforge.meta.descriptors.ValueRestriction
|
||||
import space.kscience.dataforge.meta.descriptors.allowedValues
|
||||
import space.kscience.visionforge.Colors
|
||||
import space.kscience.visionforge.widgetType
|
||||
import three.math.Color
|
||||
|
||||
|
||||
@Composable
|
||||
@ -151,7 +150,8 @@ public fun ColorValueChooser(
|
||||
value(
|
||||
value?.let { value ->
|
||||
if (value.type == ValueType.NUMBER) Colors.rgbToString(value.int)
|
||||
else "#" + Color(value.string).getHexString()
|
||||
else value.string
|
||||
//else "#" + Color(value.string).getHexString()
|
||||
} ?: "#000000"
|
||||
)
|
||||
onChange {
|
@ -225,6 +225,8 @@ public abstract interface class space/kscience/visionforge/ControlVision : space
|
||||
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 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 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;
|
||||
@ -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;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 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 {
|
||||
@ -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 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 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;
|
||||
@ -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 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 final fun getValue (Lspace/kscience/visionforge/VisionProperties;Ljava/lang/String;Ljava/lang/Boolean;Ljava/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 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;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 remove (Lspace/kscience/visionforge/MutableVisionProperties;Ljava/lang/String;)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 fun <init> ()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 setClasses (Ljava/util/List;)V
|
||||
public final fun getClasses ()Ljava/util/Set;
|
||||
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
|
||||
}
|
||||
|
||||
@ -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 static final field Companion Lspace/kscience/visionforge/html/VisionOfHtmlControl$Companion;
|
||||
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 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 {
|
||||
@ -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 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 fun <init> (Ljava/lang/String;Lspace/kscience/visionforge/html/InputFeedbackMode;)V
|
||||
public synthetic fun <init> (Ljava/lang/String;Lspace/kscience/visionforge/html/InputFeedbackMode;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
public synthetic fun <init> (ILspace/kscience/dataforge/meta/MutableMeta;Ljava/lang/String;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V
|
||||
public fun <init> (Ljava/lang/String;)V
|
||||
public final fun getDisabled ()Z
|
||||
public final fun getFeedbackMode ()Lspace/kscience/visionforge/html/InputFeedbackMode;
|
||||
public final fun getFieldName ()Ljava/lang/String;
|
||||
public final fun getInputType ()Ljava/lang/String;
|
||||
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 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 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 synthetic fun onValueChange$default (Lspace/kscience/visionforge/html/VisionOfHtmlInput;Lkotlinx/coroutines/CoroutineScope;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/Job;
|
||||
}
|
||||
|
@ -83,9 +83,32 @@ public class VisionValueChangeEvent(override val meta: Meta) : VisionControlEven
|
||||
override fun toString(): String = meta.toString()
|
||||
}
|
||||
|
||||
|
||||
public fun VisionValueChangeEvent(value: Value?, name: Name? = null): VisionValueChangeEvent = VisionValueChangeEvent(
|
||||
Meta {
|
||||
this.value = value
|
||||
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()) }
|
||||
}
|
||||
)
|
||||
|
@ -1,5 +1,7 @@
|
||||
package space.kscience.visionforge
|
||||
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import space.kscience.dataforge.context.Plugin
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
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 VisionClient.sendEventAsync(targetName: Name, event: VisionEvent): Job = context.launch {
|
||||
sendEvent(targetName, event)
|
||||
}
|
||||
|
||||
public fun VisionClient.notifyPropertyChanged(visionName: Name, propertyName: String, item: Meta?) {
|
||||
notifyPropertyChanged(visionName, propertyName.parseAsName(true), item)
|
||||
}
|
||||
|
@ -85,6 +85,7 @@ public class VisionManager(meta: Meta) : AbstractPlugin(meta), MutableVisionCont
|
||||
subclass(VisionMetaEvent.serializer())
|
||||
subclass(VisionClickEvent.serializer())
|
||||
subclass(VisionValueChangeEvent.serializer())
|
||||
subclass(VisionInputEvent.serializer())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -265,14 +265,14 @@ public abstract class AbstractVisionProperties(
|
||||
|
||||
public fun VisionProperties.getValue(
|
||||
name: String,
|
||||
inherit: Boolean? = null,
|
||||
inherit: Boolean,
|
||||
includeStyles: Boolean? = null,
|
||||
): Value? = getValue(name.parseAsName(), inherit, includeStyles)
|
||||
|
||||
/**
|
||||
* Get [Vision] property using key as a String
|
||||
*/
|
||||
public fun VisionProperties.get(
|
||||
public operator fun VisionProperties.get(
|
||||
name: String,
|
||||
inherit: Boolean? = null,
|
||||
includeStyles: Boolean? = null,
|
||||
@ -292,7 +292,7 @@ public fun MutableVisionProperties.root(
|
||||
/**
|
||||
* Get [Vision] property using key as a String
|
||||
*/
|
||||
public fun MutableVisionProperties.get(
|
||||
public operator fun MutableVisionProperties.get(
|
||||
name: String,
|
||||
inherit: Boolean? = null,
|
||||
includeStyles: Boolean? = null,
|
||||
|
@ -12,15 +12,16 @@ import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
import space.kscience.dataforge.meta.*
|
||||
import space.kscience.dataforge.names.asName
|
||||
import space.kscience.visionforge.AbstractVision
|
||||
import space.kscience.visionforge.ControlVision
|
||||
import space.kscience.visionforge.VisionControlEvent
|
||||
import space.kscience.visionforge.VisionValueChangeEvent
|
||||
import space.kscience.visionforge.*
|
||||
|
||||
|
||||
@Serializable
|
||||
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
|
||||
@ -58,6 +59,7 @@ public enum class InputFeedbackMode {
|
||||
NONE
|
||||
}
|
||||
|
||||
@Serializable
|
||||
public abstract class VisionOfHtmlControl: VisionOfHtml(), ControlVision{
|
||||
|
||||
@Transient
|
||||
@ -76,7 +78,6 @@ public abstract class VisionOfHtmlControl: VisionOfHtml(), ControlVision{
|
||||
@SerialName("html.input")
|
||||
public open class VisionOfHtmlInput(
|
||||
public val inputType: String,
|
||||
public val feedbackMode: InputFeedbackMode = InputFeedbackMode.ONCHANGE,
|
||||
) : VisionOfHtmlControl() {
|
||||
public var value: Value? by properties.value()
|
||||
public var disabled: Boolean by properties.boolean { false }
|
||||
@ -92,6 +93,11 @@ public fun VisionOfHtmlInput.onValueChange(
|
||||
callback: suspend VisionValueChangeEvent.() -> Unit,
|
||||
): 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")
|
||||
public inline fun VisionOutput.htmlInput(
|
||||
inputType: String,
|
||||
|
@ -40,7 +40,7 @@ internal class VisionPropertyTest {
|
||||
@Test
|
||||
fun testPropertyEdit() {
|
||||
val vision = manager.group()
|
||||
vision.properties.get("fff.ddd").apply {
|
||||
vision.properties["fff.ddd"].apply {
|
||||
value = 2.asValue()
|
||||
}
|
||||
assertEquals(2, vision.properties.getValue("fff.ddd")?.int)
|
||||
@ -50,7 +50,7 @@ internal class VisionPropertyTest {
|
||||
@Test
|
||||
fun testPropertyUpdate() {
|
||||
val vision = manager.group()
|
||||
vision.properties.get("fff").updateWith(TestScheme) {
|
||||
vision.properties["fff"].updateWith(TestScheme) {
|
||||
ddd = 2
|
||||
}
|
||||
assertEquals(2, vision.properties.getValue("fff.ddd")?.int)
|
||||
|
@ -1,18 +1,14 @@
|
||||
package space.kscience.visionforge
|
||||
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.dom.clear
|
||||
import kotlinx.html.InputType
|
||||
import kotlinx.html.div
|
||||
import kotlinx.html.js.input
|
||||
import org.w3c.dom.HTMLElement
|
||||
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.double
|
||||
import space.kscience.dataforge.meta.string
|
||||
import space.kscience.dataforge.names.Name
|
||||
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.
|
||||
*
|
||||
@ -62,16 +51,13 @@ internal val inputVisionRenderer: ElementVisionRenderer = ElementVisionRenderer<
|
||||
input {
|
||||
type = InputType.text
|
||||
}.also { htmlInputElement ->
|
||||
val onEvent: (Event) -> Unit = {
|
||||
client.sendInputEvent(name, htmlInputElement.value.asValue())
|
||||
|
||||
htmlInputElement.onchange = {
|
||||
client.sendEventAsync(name, VisionValueChangeEvent(htmlInputElement.value.asValue(), name))
|
||||
}
|
||||
|
||||
|
||||
when (vision.feedbackMode) {
|
||||
InputFeedbackMode.ONCHANGE -> htmlInputElement.onchange = onEvent
|
||||
|
||||
InputFeedbackMode.ONINPUT -> htmlInputElement.oninput = onEvent
|
||||
InputFeedbackMode.NONE -> {}
|
||||
htmlInputElement.oninput = {
|
||||
client.sendEventAsync(name, VisionInputEvent(htmlInputElement.value.asValue(), name))
|
||||
}
|
||||
|
||||
htmlInputElement.subscribeToInput(vision)
|
||||
@ -86,18 +72,16 @@ internal val checkboxVisionRenderer: ElementVisionRenderer =
|
||||
input {
|
||||
type = InputType.checkBox
|
||||
}.also { htmlInputElement ->
|
||||
val onEvent: (Event) -> Unit = {
|
||||
client.sendInputEvent(name, htmlInputElement.checked.asValue())
|
||||
|
||||
htmlInputElement.onchange = {
|
||||
client.sendEventAsync(name, VisionValueChangeEvent(htmlInputElement.value.asValue(), name))
|
||||
}
|
||||
|
||||
|
||||
when (vision.feedbackMode) {
|
||||
InputFeedbackMode.ONCHANGE -> htmlInputElement.onchange = onEvent
|
||||
|
||||
InputFeedbackMode.ONINPUT -> htmlInputElement.oninput = onEvent
|
||||
InputFeedbackMode.NONE -> {}
|
||||
htmlInputElement.oninput = {
|
||||
client.sendEventAsync(name, VisionInputEvent(htmlInputElement.value.asValue(), name))
|
||||
}
|
||||
|
||||
|
||||
htmlInputElement.subscribeToInput(vision)
|
||||
vision.useProperty(VisionOfCheckbox::checked) {
|
||||
htmlInputElement.checked = it ?: false
|
||||
@ -110,16 +94,13 @@ internal val textVisionRenderer: ElementVisionRenderer =
|
||||
input {
|
||||
type = InputType.text
|
||||
}.also { htmlInputElement ->
|
||||
val onEvent: (Event) -> Unit = {
|
||||
client.sendInputEvent(name, htmlInputElement.value.asValue())
|
||||
|
||||
htmlInputElement.onchange = {
|
||||
client.sendEventAsync(name, VisionValueChangeEvent(htmlInputElement.value.asValue(), name))
|
||||
}
|
||||
|
||||
|
||||
when (vision.feedbackMode) {
|
||||
InputFeedbackMode.ONCHANGE -> htmlInputElement.onchange = onEvent
|
||||
|
||||
InputFeedbackMode.ONINPUT -> htmlInputElement.oninput = onEvent
|
||||
InputFeedbackMode.NONE -> {}
|
||||
htmlInputElement.oninput = {
|
||||
client.sendEventAsync(name, VisionInputEvent(htmlInputElement.value.asValue(), name))
|
||||
}
|
||||
|
||||
htmlInputElement.subscribeToInput(vision)
|
||||
@ -135,18 +116,19 @@ internal val numberVisionRenderer: ElementVisionRenderer =
|
||||
type = InputType.number
|
||||
}.also { htmlInputElement ->
|
||||
|
||||
val onEvent: (Event) -> Unit = {
|
||||
htmlInputElement.onchange = {
|
||||
htmlInputElement.value.toDoubleOrNull()?.let {
|
||||
client.sendInputEvent(name, htmlInputElement.value.asValue())
|
||||
client.sendEventAsync(name, VisionValueChangeEvent(it.asValue(), name))
|
||||
}
|
||||
}
|
||||
|
||||
when (vision.feedbackMode) {
|
||||
InputFeedbackMode.ONCHANGE -> htmlInputElement.onchange = onEvent
|
||||
|
||||
InputFeedbackMode.ONINPUT -> htmlInputElement.oninput = onEvent
|
||||
InputFeedbackMode.NONE -> {}
|
||||
htmlInputElement.oninput = {
|
||||
htmlInputElement.value.toDoubleOrNull()?.let {
|
||||
client.sendEventAsync(name, VisionInputEvent(it.asValue(), name))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
htmlInputElement.subscribeToInput(vision)
|
||||
vision.useProperty(VisionOfNumberField::value) {
|
||||
htmlInputElement.valueAsNumber = it?.double ?: 0.0
|
||||
@ -163,18 +145,18 @@ internal val rangeVisionRenderer: ElementVisionRenderer =
|
||||
step = vision.step.toString()
|
||||
}.also { htmlInputElement ->
|
||||
|
||||
val onEvent: (Event) -> Unit = {
|
||||
htmlInputElement.onchange = {
|
||||
htmlInputElement.value.toDoubleOrNull()?.let {
|
||||
client.sendInputEvent(name, htmlInputElement.value.asValue())
|
||||
client.sendEventAsync(name, VisionValueChangeEvent(it.asValue(), name))
|
||||
}
|
||||
}
|
||||
|
||||
when (vision.feedbackMode) {
|
||||
InputFeedbackMode.ONCHANGE -> htmlInputElement.onchange = onEvent
|
||||
|
||||
InputFeedbackMode.ONINPUT -> htmlInputElement.oninput = onEvent
|
||||
InputFeedbackMode.NONE -> {}
|
||||
htmlInputElement.oninput = {
|
||||
htmlInputElement.value.toDoubleOrNull()?.let {
|
||||
client.sendEventAsync(name, VisionInputEvent(it.asValue(), name))
|
||||
}
|
||||
}
|
||||
|
||||
htmlInputElement.subscribeToInput(vision)
|
||||
vision.useProperty(VisionOfRangeField::value) {
|
||||
htmlInputElement.valueAsNumber = it?.double ?: 0.0
|
||||
|
@ -7,16 +7,7 @@ description = "Jupyter api artifact including all common modules"
|
||||
kscience {
|
||||
fullStack(
|
||||
"js/visionforge-jupyter-common.js",
|
||||
jsConfig = { useCommonJs() }
|
||||
) {
|
||||
commonWebpackConfig {
|
||||
sourceMaps = false
|
||||
cssSupport {
|
||||
enabled.set(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
)
|
||||
dependencies {
|
||||
api(projects.visionforgeSolid)
|
||||
api(projects.visionforgePlotly)
|
||||
@ -30,7 +21,6 @@ kscience {
|
||||
}
|
||||
|
||||
jsMain {
|
||||
implementation(projects.ui.ring)
|
||||
implementation(projects.visionforgeThreejs)
|
||||
}
|
||||
|
||||
|
@ -3,12 +3,12 @@ package space.kscience.visionforge.gdml.jupyter
|
||||
import space.kscience.visionforge.jupyter.VFNotebookClient
|
||||
import space.kscience.visionforge.markup.MarkupPlugin
|
||||
import space.kscience.visionforge.plotly.PlotlyPlugin
|
||||
import space.kscience.visionforge.ring.ThreeWithControlsPlugin
|
||||
import space.kscience.visionforge.runVisionClient
|
||||
import space.kscience.visionforge.solid.three.ThreePlugin
|
||||
import space.kscience.visionforge.tables.TableVisionJsPlugin
|
||||
|
||||
public fun main(): Unit = runVisionClient {
|
||||
plugin(ThreeWithControlsPlugin)
|
||||
plugin(ThreePlugin)
|
||||
plugin(PlotlyPlugin)
|
||||
plugin(MarkupPlugin)
|
||||
plugin(TableVisionJsPlugin)
|
||||
|
@ -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 getCamera ()Lspace/kscience/visionforge/solid/specifications/CameraScheme;
|
||||
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 getOnSelect ()Lkotlin/jvm/functions/Function1;
|
||||
public final fun getSize ()Lspace/kscience/visionforge/solid/specifications/CanvasSize;
|
||||
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 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 setOnSelect (Lkotlin/jvm/functions/Function1;)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 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 static final field Companion Lspace/kscience/visionforge/solid/specifications/CanvasSize$Companion;
|
||||
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 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 static final field Companion Lspace/kscience/visionforge/solid/specifications/PointScheme$Companion;
|
||||
public fun <init> ()V
|
||||
|
@ -62,7 +62,7 @@ public class Canvas3DOptions : Scheme() {
|
||||
@Suppress("DEPRECATION")
|
||||
public var axes: AxesScheme by spec(AxesScheme)
|
||||
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)
|
||||
|
||||
@ -92,7 +92,7 @@ public class Canvas3DOptions : Scheme() {
|
||||
hide()
|
||||
}
|
||||
|
||||
scheme(Canvas3DOptions::controls, ControlsScheme) {
|
||||
scheme(Canvas3DOptions::controls, Canvas3DUIScheme) {
|
||||
hide()
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
@ -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)
|
||||
}
|
@ -4,6 +4,7 @@ import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import space.kscience.dataforge.meta.getValue
|
||||
import space.kscience.dataforge.meta.int
|
||||
import space.kscience.dataforge.meta.set
|
||||
import space.kscience.dataforge.meta.string
|
||||
|
@ -9,23 +9,17 @@ kscience {
|
||||
js {
|
||||
useCommonJs()
|
||||
binaries.library()
|
||||
browser {
|
||||
commonWebpackConfig {
|
||||
cssSupport {
|
||||
enabled.set(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
|
||||
useSerialization()
|
||||
commonMain {
|
||||
api(projects.visionforgeCore)
|
||||
api("space.kscience:tables-kt:${tablesVersion}")
|
||||
}
|
||||
dependencies(jsMain) {
|
||||
jsMain {
|
||||
implementation(npm("tabulator-tables", "5.5.2"))
|
||||
implementation(npm("@types/tabulator-tables", "5.5.3"))
|
||||
}
|
||||
useSerialization()
|
||||
}
|
||||
|
||||
readme {
|
||||
|
@ -13,12 +13,11 @@ import space.kscience.visionforge.VisionPlugin
|
||||
public class TableVisionPlugin : VisionPlugin() {
|
||||
override val tag: PluginTag get() = Companion.tag
|
||||
|
||||
override val visionSerializersModule: SerializersModule
|
||||
get() = SerializersModule {
|
||||
polymorphic(Vision::class) {
|
||||
subclass(VisionOfTable.serializer())
|
||||
}
|
||||
override val visionSerializersModule: SerializersModule = SerializersModule {
|
||||
polymorphic(Vision::class) {
|
||||
subclass(VisionOfTable.serializer())
|
||||
}
|
||||
}
|
||||
|
||||
public companion object : PluginFactory<TableVisionPlugin> {
|
||||
override val tag: PluginTag = PluginTag("vision.table", PluginTag.DATAFORGE_GROUP)
|
||||
|
@ -1,22 +1,26 @@
|
||||
plugins {
|
||||
id("space.kscience.gradle.mpp")
|
||||
alias(spclibs.plugins.compose)
|
||||
}
|
||||
|
||||
kotlin{
|
||||
kotlin {
|
||||
explicitApi = org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode.Disabled
|
||||
}
|
||||
|
||||
kscience{
|
||||
js{
|
||||
kscience {
|
||||
js {
|
||||
binaries.library()
|
||||
}
|
||||
jsMain{
|
||||
dependencies {
|
||||
api(projects.visionforgeSolid)
|
||||
implementation(npm("three", "0.143.0"))
|
||||
implementation(npm("three-csg-ts", "3.1.10"))
|
||||
implementation(npm("three.meshline","1.4.0"))
|
||||
}
|
||||
|
||||
commonMain {
|
||||
api(projects.visionforgeSolid)
|
||||
api(projects.visionforgeCompose)
|
||||
}
|
||||
|
||||
jsMain {
|
||||
implementation(npm("three", "0.143.0"))
|
||||
implementation(npm("three-csg-ts", "3.1.13"))
|
||||
implementation(npm("three.meshline", "1.4.0"))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
"trackball" -> TrackballControls(camera, element)
|
||||
else -> OrbitControls(camera, element)
|
||||
|
@ -2,14 +2,15 @@ package space.kscience.visionforge.solid.three
|
||||
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import org.jetbrains.compose.web.renderComposable
|
||||
import org.w3c.dom.Element
|
||||
import org.w3c.dom.HTMLElement
|
||||
import space.kscience.dataforge.context.*
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.names.*
|
||||
import space.kscience.visionforge.*
|
||||
import space.kscience.visionforge.solid.*
|
||||
import space.kscience.visionforge.solid.specifications.Canvas3DOptions
|
||||
import space.kscience.visionforge.solid.three.compose.ThreeView
|
||||
import space.kscience.visionforge.solid.three.set
|
||||
import three.core.Object3D
|
||||
import kotlin.collections.set
|
||||
@ -21,7 +22,7 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
|
||||
|
||||
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 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> {
|
||||
return when (target) {
|
||||
ElementVisionRenderer.TYPE -> mapOf("three".asName() to this)
|
||||
@ -142,20 +134,11 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
|
||||
override fun rateVision(vision: Vision): Int =
|
||||
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) {
|
||||
renderSolid(
|
||||
element,
|
||||
vision as? Solid ?: error("Solid expected but ${vision::class} found"),
|
||||
Canvas3DOptions.read(meta)
|
||||
)
|
||||
require(vision is Solid) { "Expected Solid but found ${vision::class}" }
|
||||
renderComposable(element) {
|
||||
ThreeView(solids, vision, null, Canvas3DOptions.read(meta))
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
object3D.name = token.toString()
|
||||
add(object3D)
|
||||
|
@ -1,4 +1,4 @@
|
||||
package space.kscience.visionforge.compose
|
||||
package space.kscience.visionforge.solid.three.compose
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
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.names.Name
|
||||
import space.kscience.visionforge.Vision
|
||||
import space.kscience.visionforge.compose.*
|
||||
import space.kscience.visionforge.encodeToString
|
||||
import space.kscience.visionforge.solid.specifications.Canvas3DOptions
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -1,29 +1,24 @@
|
||||
plugins {
|
||||
id("space.kscience.gradle.mpp")
|
||||
alias(spclibs.plugins.compose)
|
||||
}
|
||||
|
||||
val ktorVersion: String by rootProject.extra
|
||||
|
||||
kscience {
|
||||
fullStack("js/visionforge-three.js") {
|
||||
commonWebpackConfig {
|
||||
cssSupport {
|
||||
enabled.set(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
fullStack("js/visionforge-three.js")
|
||||
|
||||
dependencies {
|
||||
commonMain {
|
||||
api(projects.visionforgeSolid)
|
||||
api(projects.visionforgeCompose)
|
||||
}
|
||||
|
||||
dependencies(jvmMain) {
|
||||
jvmMain{
|
||||
api(projects.visionforgeServer)
|
||||
}
|
||||
|
||||
dependencies(jsMain) {
|
||||
jsMain{
|
||||
api(projects.visionforgeThreejs)
|
||||
api(projects.ui.ring)
|
||||
compileOnly(npm("webpack-bundle-analyzer","4.5.0"))
|
||||
}
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
package space.kscience.visionforge.three
|
||||
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.visionforge.ring.ThreeWithControlsPlugin
|
||||
import space.kscience.visionforge.runVisionClient
|
||||
import space.kscience.visionforge.solid.three.ThreePlugin
|
||||
|
||||
|
||||
@DFExperimental
|
||||
public fun main(): Unit = runVisionClient {
|
||||
plugin(ThreeWithControlsPlugin)
|
||||
plugin(ThreePlugin)
|
||||
}
|
@ -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: {}
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
@ -1,10 +0,0 @@
|
||||
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
|
||||
|
||||
module.exports = {
|
||||
plugins: [
|
||||
new BundleAnalyzerPlugin({
|
||||
analyzerMode: "static",
|
||||
reportFilename: "bundle-report.html"
|
||||
})
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue
Block a user