Compare commits

..

17 Commits

Author SHA1 Message Date
9c1246fb26 Fix CME in solid children clearing. Add documentation. Splic client 2023-10-18 21:48:50 +03:00
cf6d73305b Add vision client-side events 2023-10-18 13:52:42 +03:00
6144410d22 Merge branch 'beta/1.9.20' into dev 2023-10-18 13:41:25 +03:00
2578fd6f77 Replace external enum with external sealed object 2023-10-18 11:31:11 +03:00
001efa711a update version 2023-10-18 11:30:27 +03:00
8fac827acb Update versions 2023-10-09 16:17:08 +03:00
bdc2885c0d Merge branch 'dev' into beta/1.9.20 2023-10-09 15:58:58 +03:00
d44f25ad4c Merge branch 'dev' into beta/1.9.20 2023-10-04 08:53:55 +03:00
35ec4bac0e 1.9.20-Beta 2023-09-30 09:40:12 +03:00
ba70af3a24 1.9.20-Beta 2023-09-16 14:05:19 +03:00
20fc81305c Merge branch 'dev' into beta/1.9.0
# Conflicts:
#	jupyter/visionforge-jupyter-gdml/build.gradle.kts
#	visionforge-jupyter/build.gradle.kts
#	visionforge-threejs/src/jsMain/kotlin/space/kscience/visionforge/solid/three/ThreeAxesFactory.kt
#	visionforge-threejs/src/jsMain/kotlin/space/kscience/visionforge/solid/three/ThreeGeometryBuilder.kt
#	visionforge-threejs/src/jsMain/kotlin/space/kscience/visionforge/solid/three/ThreeStlFactory.kt
2023-09-16 12:30:52 +03:00
086948492c update build plugin 2023-07-14 20:11:37 +03:00
25c3a19eed Fix demo package name to ensure proper package resolution 2023-06-27 20:30:29 +03:00
ea9d37b9a1 1.9.0-RC 2023-06-22 20:33:23 +03:00
a695e5e7c9 Use K2 2023-06-06 09:52:46 +03:00
140c59497e Change all Js modules to MPP 2023-06-02 20:58:32 +03:00
23cb8765b6 Minor fixes 2023-06-02 14:33:41 +03:00
249 changed files with 355 additions and 371 deletions

View File

@ -1,5 +1,4 @@
import org.jetbrains.kotlin.gradle.dsl.KotlinJsCompile import org.jetbrains.kotlin.gradle.dsl.KotlinJsCompile
import space.kscience.gradle.isInDevelopment
import space.kscience.gradle.useApache2Licence import space.kscience.gradle.useApache2Licence
import space.kscience.gradle.useSPCTeam import space.kscience.gradle.useSPCTeam
@ -8,7 +7,7 @@ plugins {
// id("org.jetbrains.kotlinx.kover") version "0.5.0" // id("org.jetbrains.kotlinx.kover") version "0.5.0"
} }
val dataforgeVersion by extra("0.6.1") val dataforgeVersion by extra("0.6.2")
val fxVersion by extra("11") val fxVersion by extra("11")
allprojects { allprojects {
@ -45,14 +44,7 @@ ksciencePublish {
useApache2Licence() useApache2Licence()
useSPCTeam() useSPCTeam()
} }
github(githubProject = "visionforge", githubOrg = "SciProgCentre") repository("spc","https://maven.sciprog.center/kscience")
space(
if (isInDevelopment) {
"https://maven.pkg.jetbrains.space/spc/p/sci/dev"
} else {
"https://maven.pkg.jetbrains.space/spc/p/sci/maven"
}
)
sonatype() sonatype()
} }

View File

@ -2,6 +2,8 @@ plugins {
id("space.kscience.gradle.mpp") id("space.kscience.gradle.mpp")
} }
group = "demo"
kscience { kscience {
jvm() jvm()
js { js {

View File

@ -9,30 +9,38 @@ import react.Component
import react.Props import react.Props
import react.State import react.State
external enum class DropEffects { sealed external class DropEffects {
copy, @JsName("copy")
move, object Copy : DropEffects
link,
none @JsName("move")
object Move : DropEffects
@JsName("link")
object Link : DropEffects
@JsName("none")
object None : DropEffects
} }
external interface FileDropProps: Props { external interface FileDropProps : Props {
var className: String? var className: String?
var targetClassName: String? var targetClassName: String?
var draggingOverFrameClassName: String? var draggingOverFrameClassName: String?
var draggingOverTargetClassName: String? var draggingOverTargetClassName: String?
// var frame?: Exclude<HTMLElementTagNameMap[keyof HTMLElementTagNameMap], HTMLElement> | HTMLDocument; // var frame?: Exclude<HTMLElementTagNameMap[keyof HTMLElementTagNameMap], HTMLElement> | HTMLDocument;
var onFrameDragEnter: ((event: DragEvent) -> Unit)? var onFrameDragEnter: ((event: DragEvent) -> Unit)?
var onFrameDragLeave: ((event: DragEvent) -> Unit)? var onFrameDragLeave: ((event: DragEvent) -> Unit)?
var onFrameDrop: ((event: DragEvent) -> Unit)? var onFrameDrop: ((event: DragEvent) -> Unit)?
// var onDragOver: ReactDragEventHandler<HTMLDivElement>?
// var onDragOver: ReactDragEventHandler<HTMLDivElement>?
// var onDragLeave: ReactDragEventHandler<HTMLDivElement>? // var onDragLeave: ReactDragEventHandler<HTMLDivElement>?
var onDrop: ((files: FileList?, event: dynamic) -> Unit)?//event:DragEvent<HTMLDivElement>) var onDrop: ((files: FileList?, event: dynamic) -> Unit)?//event:DragEvent<HTMLDivElement>)
var dropEffect: DropEffects? var dropEffect: DropEffects?
} }
external interface FileDropState: State { external interface FileDropState : State {
var draggingOverFrame: Boolean var draggingOverFrame: Boolean
var draggingOverTarget: Boolean var draggingOverTarget: Boolean
} }

View File

@ -2,7 +2,6 @@ package space.kscience.visionforge.gdml.demo
import drop.FileDrop import drop.FileDrop
import kotlinx.css.* import kotlinx.css.*
import kotlinx.css.properties.border
import org.w3c.files.FileList import org.w3c.files.FileList
import react.RBuilder import react.RBuilder
import styled.css import styled.css
@ -13,7 +12,7 @@ import styled.styledDiv
fun RBuilder.fileDrop(title: String, action: (files: FileList?) -> Unit) { fun RBuilder.fileDrop(title: String, action: (files: FileList?) -> Unit) {
styledDiv { styledDiv {
css { css {
border(style = BorderStyle.dashed, width = 1.px, color = Color.orange) border = Border(style = BorderStyle.dashed, width = 1.px, color = Color.orange)
flexGrow = 0.0 flexGrow = 0.0
alignContent = Align.center alignContent = Align.center
} }

View File

@ -1,19 +1,19 @@
plugins { plugins {
id("space.kscience.gradle.js") id("space.kscience.gradle.mpp")
} }
kscience{ kscience {
useCoroutines() useCoroutines()
} }
kotlin{ kotlin {
explicitApi = null explicitApi = null
js{ js {
useCommonJs() useCommonJs()
browser { browser {
binaries.executable() binaries.executable()
commonWebpackConfig { commonWebpackConfig {
cssSupport{ cssSupport {
enabled.set(false) enabled.set(false)
} }
} }
@ -21,11 +21,15 @@ kotlin{
} }
} }
kscience {
dependencies{ dependencies {
implementation(projects.visionforge.visionforgeGdml) implementation(projects.visionforge.visionforgeGdml)
implementation(projects.visionforge.visionforgePlotly) implementation(projects.visionforge.visionforgePlotly)
implementation(projects.visionforge.visionforgeMarkdown) implementation(projects.visionforge.visionforgeMarkdown)
implementation(projects.visionforge.visionforgeThreejs) implementation(projects.visionforge.visionforgeThreejs)
implementation(projects.ui.ring) }
jsMain {
implementation(projects.ui.ring)
}
} }

View File

@ -8,7 +8,7 @@ import space.kscience.plotly.models.Trace
import space.kscience.plotly.scatter 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.VisionClient import space.kscience.visionforge.JsVisionClient
import space.kscience.visionforge.plotly.PlotlyPlugin import space.kscience.visionforge.plotly.PlotlyPlugin
import space.kscience.visionforge.react.createRoot import space.kscience.visionforge.react.createRoot
import space.kscience.visionforge.react.render import space.kscience.visionforge.react.render
@ -34,7 +34,7 @@ private class JsPlaygroundApp : Application {
val playgroundContext = Context { val playgroundContext = Context {
plugin(ThreeWithControlsPlugin) plugin(ThreeWithControlsPlugin)
plugin(VisionClient) plugin(JsVisionClient)
plugin(PlotlyPlugin) plugin(PlotlyPlugin)
} }
@ -43,8 +43,8 @@ private class JsPlaygroundApp : Application {
createRoot(element).render { createRoot(element).render {
styledDiv { styledDiv {
css { css {
padding(0.pt) padding = Padding(0.pt)
margin(0.pt) margin = Margin(0.pt)
height = 100.vh height = 100.vh
width = 100.vw width = 100.vw
} }

View File

@ -1,5 +1,4 @@
import kotlinx.css.* import kotlinx.css.*
import kotlinx.css.properties.border
import kotlinx.dom.clear import kotlinx.dom.clear
import kotlinx.html.dom.append import kotlinx.html.dom.append
import org.intellij.markdown.flavours.commonmark.CommonMarkFlavourDescriptor import org.intellij.markdown.flavours.commonmark.CommonMarkFlavourDescriptor
@ -45,10 +44,10 @@ val Markup = fc<MarkupProps>("Markup") { props ->
css { css {
width = 100.pct width = 100.pct
height = 100.pct height = 100.pct
border(2.pt, BorderStyle.solid, Color.blue) border= Border(2.pt, BorderStyle.solid, Color.blue)
padding(left = 8.pt) padding = Padding(left = 8.pt)
backgroundColor = Color.white backgroundColor = Color.white
flex(1.0) flex = Flex(1.0)
zIndex = 10000 zIndex = 10000
} }
ref = elementRef ref = elementRef

View File

@ -1,5 +1,4 @@
import kotlinx.css.* import kotlinx.css.*
import kotlinx.css.properties.border
import org.w3c.dom.Element import org.w3c.dom.Element
import org.w3c.dom.HTMLElement import org.w3c.dom.HTMLElement
import react.* import react.*
@ -30,8 +29,8 @@ val Plotly = fc<PlotlyProps>("Plotly") { props ->
css { css {
width = 100.pct width = 100.pct
height = 100.pct height = 100.pct
border(2.pt, BorderStyle.solid, Color.blue) border = Border(2.pt, BorderStyle.solid, Color.blue)
flex(1.0) flex = Flex(1.0)
} }
ref = elementRef ref = elementRef
} }

View File

@ -13,6 +13,7 @@ kscience {
useKtor() useKtor()
fullStack( fullStack(
"muon-monitor.js", "muon-monitor.js",
development = true,
jvmConfig = { withJava() }, jvmConfig = { withJava() },
jsConfig = { useCommonJs() } jsConfig = { useCommonJs() }
) { ) {
@ -34,8 +35,8 @@ kscience {
implementation("ch.qos.logback:logback-classic:1.2.11") implementation("ch.qos.logback:logback-classic:1.2.11")
} }
jsMain { jsMain {
implementation(project(":ui:ring")) implementation(projects.ui.ring)
implementation(project(":visionforge-threejs")) implementation(projects.visionforgeThreejs)
//implementation(devNpm("webpack-bundle-analyzer", "4.4.0")) //implementation(devNpm("webpack-bundle-analyzer", "4.4.0"))
} }
} }

View File

@ -3,6 +3,7 @@ package ru.mipt.npm.muon.monitor
import ru.mipt.npm.muon.monitor.Monitor.CENTRAL_LAYER_Z import ru.mipt.npm.muon.monitor.Monitor.CENTRAL_LAYER_Z
import ru.mipt.npm.muon.monitor.Monitor.LOWER_LAYER_Z import ru.mipt.npm.muon.monitor.Monitor.LOWER_LAYER_Z
import ru.mipt.npm.muon.monitor.Monitor.UPPER_LAYER_Z import ru.mipt.npm.muon.monitor.Monitor.UPPER_LAYER_Z
import space.kscience.dataforge.names.asName
import space.kscience.visionforge.MutableVisionContainer import space.kscience.visionforge.MutableVisionContainer
import space.kscience.visionforge.VisionManager import space.kscience.visionforge.VisionManager
import space.kscience.visionforge.setAsRoot import space.kscience.visionforge.setAsRoot
@ -34,7 +35,7 @@ class Model(val manager: VisionManager) {
} }
} }
var tracks: SolidGroup val tracks: SolidGroup = SolidGroup()
val root: SolidGroup = SolidGroup().apply { val root: SolidGroup = SolidGroup().apply {
setAsRoot(this@Model.manager) setAsRoot(this@Model.manager)
@ -59,7 +60,8 @@ class Model(val manager: VisionManager) {
detector(it) detector(it)
} }
} }
tracks = solidGroup("tracks")
setChild("tracks".asName(), tracks)
} }
private fun highlight(pixel: String) { private fun highlight(pixel: String) {

View File

@ -17,7 +17,7 @@ kotlin {
useCommonJs() useCommonJs()
browser { browser {
webpackTask { webpackTask {
outputFileName = "js/visionforge-playground.js" mainOutputFileName.set("js/visionforge-playground.js")
} }
commonWebpackConfig { commonWebpackConfig {
sourceMaps = true sourceMaps = true

View File

@ -14,6 +14,7 @@ import space.kscience.visionforge.html.ResourceLocation
import space.kscience.visionforge.solid.* import space.kscience.visionforge.solid.*
import java.util.zip.ZipInputStream import java.util.zip.ZipInputStream
import kotlin.io.path.Path import kotlin.io.path.Path
import kotlin.io.path.createDirectories
import kotlin.io.path.writeText import kotlin.io.path.writeText
@ -25,6 +26,8 @@ private fun Meta.countTypes(): Sequence<String> = sequence {
} }
fun main() { fun main() {
Path("data").createDirectories()
val string = ZipInputStream(TGeoManager::class.java.getResourceAsStream("/root/geometry_run_7-2076.zip")!!).use { val string = ZipInputStream(TGeoManager::class.java.getResourceAsStream("/root/geometry_run_7-2076.zip")!!).use {
it.nextEntry it.nextEntry
it.readAllBytes().decodeToString() it.readAllBytes().decodeToString()

View File

@ -6,5 +6,8 @@ kotlin.js.compiler=ir
org.gradle.parallel=true org.gradle.parallel=true
org.gradle.jvmargs=-Xmx4G org.gradle.jvmargs=-Xmx4G
toolsVersion=0.14.9-kotlin-1.8.20 org.jetbrains.compose.experimental.jscanvas.enabled=true
org.jetbrains.compose.experimental.jscanvas.enabled=true
toolsVersion=0.15.0-kotlin-1.9.20-RC
#kotlin.experimental.tryK2=true
#kscience.wasm.disabled=true

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

View File

@ -1,15 +1,19 @@
plugins { plugins {
kotlin("js") id("space.kscience.gradle.mpp")
id("space.kscience.gradle.js")
} }
val dataforgeVersion: String by rootProject.extra val dataforgeVersion: String by rootProject.extra
dependencies { kscience{
api(project(":visionforge-solid")) js()
api(project(":ui:react")) jsMain{
implementation(npm("file-saver", "2.0.2")) dependencies {
implementation(npm("bootstrap","4.6.0")) api(project(":visionforge-solid"))
implementation(npm("jquery","3.5.1")) api(project(":ui:react"))
implementation(npm("popper.js","1.16.1")) implementation(npm("file-saver", "2.0.2"))
implementation(npm("bootstrap","4.6.0"))
implementation(npm("jquery","3.5.1"))
implementation(npm("popper.js","1.16.1"))
}
}
} }

View File

@ -1,8 +1,8 @@
package space.kscience.visionforge.bootstrap package space.kscience.visionforge.bootstrap
public fun useBootstrap(){ public fun useBootstrap(){
kotlinext.js.require("bootstrap/dist/css/bootstrap.min.css") kotlinext.js.require<dynamic>("bootstrap/dist/css/bootstrap.min.css")
kotlinext.js.require("bootstrap") kotlinext.js.require<dynamic>("bootstrap")
} }
//public inline fun TagConsumer<HTMLElement>.card(title: String, crossinline block: TagConsumer<HTMLElement>.() -> Unit) { //public inline fun TagConsumer<HTMLElement>.card(title: String, crossinline block: TagConsumer<HTMLElement>.() -> Unit) {

View File

@ -2,13 +2,9 @@ package space.kscience.visionforge.bootstrap
import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.css.BorderStyle import kotlinx.css.*
import kotlinx.css.Color
import kotlinx.css.padding
import kotlinx.css.properties.border
import kotlinx.css.px
import kotlinx.html.js.onClickFunction import kotlinx.html.js.onClickFunction
import kotlinx.html.org.w3c.dom.events.Event import org.w3c.dom.events.Event
import org.w3c.files.Blob import org.w3c.files.Blob
import org.w3c.files.BlobPropertyBag import org.w3c.files.BlobPropertyBag
import react.FC import react.FC
@ -29,7 +25,7 @@ private fun saveData(event: Event, fileName: String, mimeType: String = "text/pl
event.stopPropagation(); event.stopPropagation();
event.preventDefault(); event.preventDefault();
val fileSaver = kotlinext.js.require("file-saver") val fileSaver = kotlinext.js.require<dynamic>("file-saver")
val blob = Blob(arrayOf(dataBuilder()), BlobPropertyBag("$mimeType;charset=utf-8")) val blob = Blob(arrayOf(dataBuilder()), BlobPropertyBag("$mimeType;charset=utf-8"))
fileSaver.saveAs(blob, fileName) fileSaver.saveAs(blob, fileName)
} }
@ -53,8 +49,8 @@ public val CanvasControls: FC<CanvasControlsProps> = fc("CanvasControls") { prop
flexColumn { flexColumn {
flexRow { flexRow {
css { css {
border(1.px, BorderStyle.solid, Color.blue) border = Border(1.px, BorderStyle.solid, Color.blue)
padding(4.px) padding = Padding(4.px)
} }
props.vision?.let { vision -> props.vision?.let { vision ->
button { button {

View File

@ -1,7 +1,6 @@
package space.kscience.visionforge.bootstrap package space.kscience.visionforge.bootstrap
import kotlinx.css.* import kotlinx.css.*
import kotlinx.css.properties.border
import react.FC import react.FC
import react.PropsWithChildren import react.PropsWithChildren
import react.RBuilder import react.RBuilder
@ -33,13 +32,13 @@ public val ThreeControls: FC<ThreeControlsProps> = fc { props ->
} }
tab("Tree") { tab("Tree") {
css { css {
border(1.px, BorderStyle.solid, Color.lightGray) border = Border(1.px, BorderStyle.solid, Color.lightGray)
padding(10.px) padding = Padding(10.px)
} }
h2 { +"Object tree" } h2 { +"Object tree" }
styledDiv { styledDiv {
css { css {
flex(1.0, 1.0, FlexBasis.inherit) flex = Flex(1.0, 1.0, FlexBasis.inherit)
} }
props.vision?.let { props.vision?.let {
visionTree(it, props.selected, props.onSelect) visionTree(it, props.selected, props.onSelect)

View File

@ -1,11 +1,16 @@
plugins { plugins {
id("space.kscience.gradle.js") id("space.kscience.gradle.mpp")
} }
dependencies{ kscience {
api(project(":visionforge-solid")) js()
api("org.jetbrains.kotlin-wrappers:kotlin-styled") jsMain {
api("org.jetbrains.kotlin-wrappers:kotlin-react-dom") dependencies {
api(projects.visionforgeSolid)
api("org.jetbrains.kotlin-wrappers:kotlin-styled")
api("org.jetbrains.kotlin-wrappers:kotlin-react-dom")
// implementation(npm("react-select","4.3.0")) // implementation(npm("react-select","4.3.0"))
implementation(project(":visionforge-threejs")) api(projects.visionforgeThreejs)
}
}
} }

View File

@ -3,7 +3,7 @@ package space.kscience.visionforge.react
import kotlinx.css.Align import kotlinx.css.Align
import kotlinx.css.alignItems import kotlinx.css.alignItems
import kotlinx.html.js.onClickFunction import kotlinx.html.js.onClickFunction
import kotlinx.html.org.w3c.dom.events.Event import org.w3c.dom.events.Event
import react.* import react.*
import react.dom.a import react.dom.a
import react.dom.attrs import react.dom.attrs

View File

@ -1,10 +1,10 @@
package space.kscience.visionforge.react package space.kscience.visionforge.react
import kotlinx.html.js.onChangeFunction import kotlinx.html.js.onChangeFunction
import kotlinx.html.org.w3c.dom.events.Event
import org.w3c.dom.HTMLOptionElement import org.w3c.dom.HTMLOptionElement
import org.w3c.dom.HTMLSelectElement import org.w3c.dom.HTMLSelectElement
import org.w3c.dom.asList import org.w3c.dom.asList
import org.w3c.dom.events.Event
import react.FC import react.FC
import react.dom.attrs import react.dom.attrs
import react.dom.option import react.dom.option

View File

@ -10,13 +10,16 @@ import kotlinx.coroutines.launch
import kotlinx.css.* import kotlinx.css.*
import kotlinx.css.properties.TextDecoration import kotlinx.css.properties.TextDecoration
import kotlinx.html.js.onClickFunction import kotlinx.html.js.onClickFunction
import kotlinx.html.org.w3c.dom.events.Event import org.w3c.dom.events.Event
import react.* import react.*
import react.dom.attrs import react.dom.attrs
import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.MutableMeta
import space.kscience.dataforge.meta.ObservableMutableMeta
import space.kscience.dataforge.meta.descriptors.MetaDescriptor import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.meta.descriptors.ValueRequirement import space.kscience.dataforge.meta.descriptors.ValueRequirement
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.remove
import space.kscience.dataforge.names.* import space.kscience.dataforge.names.*
import space.kscience.visionforge.hidden import space.kscience.visionforge.hidden
import styled.css import styled.css
@ -148,7 +151,7 @@ private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) {
css { css {
//+TreeStyles.resizeableInput //+TreeStyles.resizeableInput
width = 160.px width = 160.px
margin(1.px, 5.px) margin = Margin(1.px, 5.px)
} }
ValueChooser { ValueChooser {
attrs { attrs {
@ -167,7 +170,7 @@ private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) {
css { css {
width = 24.px width = 24.px
alignSelf = Align.stretch alignSelf = Align.stretch
margin(1.px, 5.px) margin = Margin(1.px, 5.px)
backgroundColor = Color.white backgroundColor = Color.white
borderStyle = BorderStyle.solid borderStyle = BorderStyle.solid
borderRadius = 2.px borderRadius = 2.px

View File

@ -4,8 +4,9 @@ import kotlinx.css.pct
import kotlinx.css.width import kotlinx.css.width
import kotlinx.html.InputType import kotlinx.html.InputType
import kotlinx.html.js.onChangeFunction import kotlinx.html.js.onChangeFunction
import kotlinx.html.org.w3c.dom.events.Event import kotlinx.html.js.onInputFunction
import org.w3c.dom.HTMLInputElement import org.w3c.dom.HTMLInputElement
import org.w3c.dom.events.Event
import react.FC import react.FC
import react.dom.attrs import react.dom.attrs
import react.fc import react.fc
@ -58,8 +59,8 @@ public val RangeValueChooser: FC<ValueChooserProps> = fc("RangeValueChooser") {
attrs { attrs {
disabled = rangeDisabled disabled = rangeDisabled
value = innerValue?.toString() ?: "" value = innerValue?.toString() ?: ""
onChangeFunction = handleChange // onChangeFunction = handleChange
consumer.onTagEvent(this, "input", handleChange) onInputFunction = handleChange
val minValue = props.descriptor?.attributes?.get("min").string val minValue = props.descriptor?.attributes?.get("min").string
minValue?.let { minValue?.let {
min = it min = it

View File

@ -52,10 +52,10 @@ public object TreeStyles : StyleSheet("treeStyles", true) {
} }
public val treeLabel:RuleSet by css { public val treeLabel:RuleSet by css {
border = "none" border = Border.none
padding(left = 4.pt, right = 4.pt, top = 0.pt, bottom = 0.pt) padding = Padding(left = 4.pt, right = 4.pt, top = 0.pt, bottom = 0.pt)
textAlign = TextAlign.left textAlign = TextAlign.left
flex(1.0) flex = Flex(1.0)
} }
public val treeLabelInactive: RuleSet by css { public val treeLabelInactive: RuleSet by css {

View File

@ -1,13 +1,10 @@
package space.kscience.visionforge.react package space.kscience.visionforge.react
import kotlinx.css.Color import kotlinx.css.*
import kotlinx.css.Cursor import kotlinx.css.properties.TextDecoration
import kotlinx.css.color
import kotlinx.css.cursor
import kotlinx.css.properties.TextDecorationLine import kotlinx.css.properties.TextDecorationLine
import kotlinx.css.properties.textDecoration
import kotlinx.html.js.onClickFunction import kotlinx.html.js.onClickFunction
import kotlinx.html.org.w3c.dom.events.Event import org.w3c.dom.events.Event
import react.* import react.*
import react.dom.attrs import react.dom.attrs
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
@ -37,7 +34,7 @@ private val TreeLabel = fc<ObjectTreeProps> { props ->
color = Color("#069") color = Color("#069")
cursor = Cursor.pointer cursor = Cursor.pointer
hover { hover {
textDecoration(TextDecorationLine.underline) textDecoration = TextDecoration(setOf(TextDecorationLine.underline))
} }
if (props.name == props.selected) { if (props.name == props.selected) {
+TreeStyles.treeLabelSelected +TreeStyles.treeLabelSelected

View File

@ -1,15 +1,12 @@
package space.kscience.visionforge.react package space.kscience.visionforge.react
import kotlinx.css.margin import kotlinx.css.*
import kotlinx.css.pct
import kotlinx.css.px
import kotlinx.css.width
import kotlinx.html.InputType import kotlinx.html.InputType
import kotlinx.html.js.onChangeFunction import kotlinx.html.js.onChangeFunction
import kotlinx.html.js.onKeyDownFunction import kotlinx.html.js.onKeyDownFunction
import kotlinx.html.org.w3c.dom.events.Event
import org.w3c.dom.HTMLInputElement import org.w3c.dom.HTMLInputElement
import org.w3c.dom.HTMLSelectElement import org.w3c.dom.HTMLSelectElement
import org.w3c.dom.events.Event
import react.FC import react.FC
import react.Props import react.Props
import react.dom.attrs import react.dom.attrs
@ -145,7 +142,7 @@ public val ColorValueChooser: FC<ValueChooserProps> = fc("ColorValueChooser") {
styledInput(type = InputType.color) { styledInput(type = InputType.color) {
css { css {
width = 100.pct width = 100.pct
margin(0.px) margin = Margin(0.px)
} }
attrs { attrs {
this.value = props.value?.let { value -> this.value = props.value?.let { value ->

View File

@ -1,11 +1,11 @@
plugins { plugins {
id("space.kscience.gradle.js") id("space.kscience.gradle.mpp")
} }
val dataforgeVersion: String by rootProject.extra val dataforgeVersion: String by rootProject.extra
kotlin{ kscience{
js(IR){ js{
useCommonJs() useCommonJs()
browser { browser {
commonWebpackConfig { commonWebpackConfig {
@ -15,12 +15,11 @@ kotlin{
} }
} }
} }
} jsMain{
api(projects.ui.react)
api("org.jetbrains.kotlin-wrappers:kotlin-ring-ui")
dependencies{ implementation(npm("core-js","3.12.1"))
api(project(":ui:react")) implementation(npm("file-saver", "2.0.2"))
api("org.jetbrains.kotlin-wrappers:kotlin-ring-ui") }
implementation(npm("core-js","3.12.1"))
implementation(npm("file-saver", "2.0.2"))
} }

View File

@ -130,7 +130,7 @@ public val ThreeCanvasWithControls: FC<ThreeCanvasWithControlsProps> = fc("Three
css { css {
height = 100.pct height = 100.pct
minWidth = 600.px minWidth = 600.px
flex(10.0, 1.0, FlexBasis("600px")) flex = Flex(10.0, 1.0, FlexBasis("600px"))
position = Position.relative position = Position.relative
} }
@ -199,11 +199,11 @@ public val ThreeCanvasWithControls: FC<ThreeCanvasWithControlsProps> = fc("Three
} }
flexColumn { flexColumn {
css { css {
padding(4.px) padding = Padding(4.px)
minWidth = 400.px minWidth = 400.px
height = 100.pct height = 100.pct
overflowY = Overflow.auto overflowY = Overflow.auto
flex(1.0, 10.0, FlexBasis("300px")) flex = Flex(1.0, 10.0, FlexBasis("300px"))
} }
ringThreeControls(options, solid, selected, onSelect, additionalTabs = props.additionalTabs) ringThreeControls(options, solid, selected, onSelect, additionalTabs = props.additionalTabs)
} }

View File

@ -2,13 +2,9 @@ package space.kscience.visionforge.ring
import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.css.BorderStyle import kotlinx.css.*
import kotlinx.css.Color
import kotlinx.css.padding
import kotlinx.css.properties.border
import kotlinx.css.px
import kotlinx.html.js.onClickFunction import kotlinx.html.js.onClickFunction
import kotlinx.html.org.w3c.dom.events.Event import org.w3c.dom.events.Event
import org.w3c.files.Blob import org.w3c.files.Blob
import org.w3c.files.BlobPropertyBag import org.w3c.files.BlobPropertyBag
import react.FC import react.FC
@ -34,7 +30,7 @@ internal fun saveData(event: Event, fileName: String, mimeType: String = "text/p
event.stopPropagation(); event.stopPropagation();
event.preventDefault(); event.preventDefault();
val fileSaver = kotlinext.js.require("file-saver") val fileSaver = kotlinext.js.require<dynamic>("file-saver")
val blob = Blob(arrayOf(dataBuilder()), BlobPropertyBag("$mimeType;charset=utf-8")) val blob = Blob(arrayOf(dataBuilder()), BlobPropertyBag("$mimeType;charset=utf-8"))
fileSaver.saveAs(blob, fileName) fileSaver.saveAs(blob, fileName)
} }
@ -58,8 +54,8 @@ internal val CanvasControls: FC<CanvasControlsProps> = fc("CanvasControls") { pr
flexColumn { flexColumn {
flexRow { flexRow {
css { css {
border(1.px, BorderStyle.solid, Color.blue) border = Border(1.px, BorderStyle.solid, Color.blue)
padding(4.px) padding = Padding(4.px)
} }
props.vision?.let { vision -> props.vision?.let { vision ->
button { button {

View File

@ -9,12 +9,34 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withLock
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.*
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.isEmpty import space.kscience.dataforge.names.isEmpty
import space.kscience.dataforge.names.plus import space.kscience.dataforge.names.plus
import kotlin.time.Duration import kotlin.time.Duration
/**
* A vision used only in change propagation and showing that the target should be removed
*/
@Serializable
@SerialName("null")
public object NullVision : Vision {
override var parent: Vision?
get() = null
set(_) {
error("Can't set parent for null vision")
}
override val properties: MutableVisionProperties get() = error("Can't get properties of `NullVision`")
override val descriptor: MetaDescriptor? = null
}
/** /**
* Create a deep copy of given Vision without external connections. * Create a deep copy of given Vision without external connections.
*/ */
@ -28,6 +50,22 @@ private fun Vision.deepCopy(manager: VisionManager): Vision {
} }
/**
* An event that contains changes made to a vision.
*
* @param vision a new value for vision content. If the Vision is to be removed should be [NullVision]
* @param properties updated properties
* @param children a map of children changed in ths [VisionChange].
*/
@Serializable
@SerialName("change")
public data class VisionChange(
public val vision: Vision? = null,
public val properties: Meta? = null,
public val children: Map<Name, VisionChange>? = null,
)
/** /**
* An update for a [Vision] * An update for a [Vision]
*/ */

View File

@ -0,0 +1,42 @@
package space.kscience.visionforge
import kotlinx.coroutines.launch
import space.kscience.dataforge.context.Plugin
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.MetaRepr
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.parseAsName
/**
* A feedback client that communicates with a server and provides ability to propagate events and changes back to the model
*/
public interface VisionClient: Plugin {
public val visionManager: VisionManager
public suspend fun sendEvent(event: VisionEvent)
public fun notifyPropertyChanged(visionName: Name, propertyName: Name, item: Meta?)
}
public fun VisionClient.notifyPropertyChanged(visionName: Name, propertyName: String, item: Meta?) {
notifyPropertyChanged(visionName, propertyName.parseAsName(true), item)
}
public fun VisionClient.notifyPropertyChanged(visionName: Name, propertyName: String, item: Number) {
notifyPropertyChanged(visionName, propertyName.parseAsName(true), Meta(item))
}
public fun VisionClient.notifyPropertyChanged(visionName: Name, propertyName: String, item: String) {
notifyPropertyChanged(visionName, propertyName.parseAsName(true), Meta(item))
}
public fun VisionClient.notifyPropertyChanged(visionName: Name, propertyName: String, item: Boolean) {
notifyPropertyChanged(visionName, propertyName.parseAsName(true), Meta(item))
}
public fun VisionClient.sendEvent(visionName: Name, event: MetaRepr): Unit {
context.launch {
sendEvent(VisionMetaEvent(visionName, event.toMeta()))
}
}

View File

@ -187,30 +187,10 @@ internal abstract class VisionChildrenImpl(
} }
override fun clear() { override fun clear() {
items?.forEach { set(it.key, null) } items?.clear()
// if (!items.isNullOrEmpty()) { updateJobs.values.forEach { it.cancel() }
// updateJobs.values.forEach { updateJobs.clear()
// it.cancel() onChange(Name.EMPTY)
// }
// updateJobs.clear()
// items?.clear()
// }
} }
} }
//
//internal object VisionChildrenContainerSerializer : KSerializer<MutableVisionChildren> {
// private val mapSerializer = serializer<Map<NameToken, Vision>>()
//
// override val descriptor: SerialDescriptor = mapSerializer.descriptor
//
// override fun deserialize(decoder: Decoder): MutableVisionChildren {
// val map = decoder.decodeSerializableValue(mapSerializer)
// return VisionChildrenImpl(map)
// }
//
// override fun serialize(encoder: Encoder, value: MutableVisionChildren) {
// val map = value.keys.associateWith { value[it]!! }
// encoder.encodeSerializableValue(mapSerializer, map)
// }
//
//}

View File

@ -3,50 +3,38 @@ package space.kscience.visionforge
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.descriptors.MetaDescriptor import space.kscience.dataforge.meta.MutableMeta
import space.kscience.dataforge.meta.set
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
/** /**
* An event propagated from client to a server * An event propagated from client to a server
*/ */
@Serializable @Serializable
public sealed interface VisionEvent public sealed interface VisionEvent{
public val targetName: Name
public companion object{
public val CLICK_EVENT_KEY: Name get() = Name.of("events", "click", "payload")
}
}
/** /**
* An event that consists of custom meta * An event that consists of custom meta
*/ */
@Serializable @Serializable
@SerialName("meta") @SerialName("meta")
public class VisionMetaEvent(public val targetName: Name, public val meta: Meta) : VisionEvent public class VisionMetaEvent(override val targetName: Name, public val meta: Meta) : VisionEvent
/**
* A vision used only in change propagation and showing that the target should be removed
*/
@Serializable
@SerialName("null")
public object NullVision : Vision {
override var parent: Vision?
get() = null
set(_) {
error("Can't set parent for null vision")
}
override val properties: MutableVisionProperties get() = error("Can't get properties of `NullVision`")
override val descriptor: MetaDescriptor? = null
}
/**
* @param vision a new value for vision content. If the Vision is to be removed should be [NullVision]
* @param properties updated properties
* @param children a map of children changed in ths [VisionChange].
*/
@Serializable @Serializable
@SerialName("change") @SerialName("change")
public data class VisionChange( public class VisionChangeEvent(override val targetName: Name, public val change: VisionChange) : VisionEvent
public val vision: Vision? = null,
public val properties: Meta? = null, public val Vision.Companion.CLICK_EVENT_KEY: Name get() = Name.of("events", "click", "payload")
public val children: Map<Name, VisionChange>? = null,
) : VisionEvent /**
* Set the payload to be sent to server on click
*/
public fun Vision.onClickPayload(payloadBuilder: MutableMeta.() -> Unit){
properties[VisionEvent.CLICK_EVENT_KEY] = Meta(payloadBuilder)
}

View File

@ -5,6 +5,9 @@ import kotlinx.browser.window
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.isActive import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
@ -12,7 +15,10 @@ import kotlinx.coroutines.sync.withLock
import org.w3c.dom.* import org.w3c.dom.*
import org.w3c.dom.url.URL import org.w3c.dom.url.URL
import space.kscience.dataforge.context.* import space.kscience.dataforge.context.*
import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.MetaSerializer
import space.kscience.dataforge.meta.get
import space.kscience.dataforge.meta.int
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.parseAsName import space.kscience.dataforge.names.parseAsName
import space.kscience.visionforge.html.VisionTagConsumer import space.kscience.visionforge.html.VisionTagConsumer
@ -26,9 +32,9 @@ import kotlin.time.Duration.Companion.milliseconds
/** /**
* A Kotlin-browser plugin that renders visions based on provided renderers and governs communication with the server. * A Kotlin-browser plugin that renders visions based on provided renderers and governs communication with the server.
*/ */
public class VisionClient : AbstractPlugin() { public class JsVisionClient : AbstractPlugin(), VisionClient {
override val tag: PluginTag get() = Companion.tag override val tag: PluginTag get() = Companion.tag
private val visionManager: VisionManager by require(VisionManager) override val visionManager: VisionManager by require(VisionManager)
/** /**
* Up-going tree traversal in search for endpoint attribute. If element is null, return window URL * Up-going tree traversal in search for endpoint attribute. If element is null, return window URL
@ -66,7 +72,7 @@ public class VisionClient : AbstractPlugin() {
/** /**
* Communicate vision property changed from rendering engine to model * Communicate vision property changed from rendering engine to model
*/ */
public fun notifyPropertyChanged(visionName: Name, propertyName: Name, item: Meta?) { override fun notifyPropertyChanged(visionName: Name, propertyName: Name, item: Meta?) {
context.launch { context.launch {
mutex.withLock { mutex.withLock {
changeCollector.propertyChanged(visionName, propertyName, item) changeCollector.propertyChanged(visionName, propertyName, item)
@ -82,7 +88,7 @@ public class VisionClient : AbstractPlugin() {
/** /**
* Send a custom feedback event * Send a custom feedback event
*/ */
public suspend fun sendEvent(event: VisionEvent) { override suspend fun sendEvent(event: VisionEvent) {
eventCollector.emit(event) eventCollector.emit(event)
} }
@ -141,21 +147,19 @@ public class VisionClient : AbstractPlugin() {
onopen = { onopen = {
feedbackJob = visionManager.context.launch { feedbackJob = visionManager.context.launch {
eventCollector.filter { it.targetName == name }.onEach {
send(visionManager.jsonFormat.encodeToString(VisionEvent.serializer(), it))
}.launchIn(this)
while (isActive) { while (isActive) {
delay(feedbackAggregationTime.milliseconds) delay(feedbackAggregationTime.milliseconds)
val change = changeCollector[name] ?: continue val change = changeCollector[name] ?: continue
if (!change.isEmpty()) { if (!change.isEmpty()) {
mutex.withLock { mutex.withLock {
send(change.toJsonString(visionManager)) eventCollector.emit(VisionChangeEvent(name, change.deepCopy(visionManager)))
change.reset() change.reset()
} }
} }
// // take channel for given vision name
// eventCollector[name]?.let { channel ->
// for (e in channel) {
// send(visionManager.jsonFormat.encodeToString(VisionEvent.serializer(), e))
// }
// }
} }
} }
logger.info { "WebSocket feedback channel established for output '$name'" } logger.info { "WebSocket feedback channel established for output '$name'" }
@ -251,37 +255,15 @@ public class VisionClient : AbstractPlugin() {
textVisionRenderer(this), textVisionRenderer(this),
formVisionRenderer(this) formVisionRenderer(this)
).associateByName() ).associateByName()
} else super.content(target) } else super<AbstractPlugin>.content(target)
public companion object : PluginFactory<VisionClient> { public companion object : PluginFactory<JsVisionClient> {
override fun build(context: Context, meta: Meta): VisionClient = VisionClient() override fun build(context: Context, meta: Meta): JsVisionClient = JsVisionClient()
override val tag: PluginTag = PluginTag(name = "vision.client.js", group = PluginTag.DATAFORGE_GROUP) override val tag: PluginTag = PluginTag(name = "vision.client.js", group = PluginTag.DATAFORGE_GROUP)
} }
} }
public fun VisionClient.notifyPropertyChanged(visionName: Name, propertyName: String, item: Meta?) {
notifyPropertyChanged(visionName, propertyName.parseAsName(true), item)
}
public fun VisionClient.notifyPropertyChanged(visionName: Name, propertyName: String, item: Number) {
notifyPropertyChanged(visionName, propertyName.parseAsName(true), Meta(item))
}
public fun VisionClient.notifyPropertyChanged(visionName: Name, propertyName: String, item: String) {
notifyPropertyChanged(visionName, propertyName.parseAsName(true), Meta(item))
}
public fun VisionClient.notifyPropertyChanged(visionName: Name, propertyName: String, item: Boolean) {
notifyPropertyChanged(visionName, propertyName.parseAsName(true), Meta(item))
}
public fun VisionClient.sendEvent(visionName: Name, event: MetaRepr): Unit {
context.launch {
sendEvent(VisionMetaEvent(visionName, event.toMeta()))
}
}
private fun whenDocumentLoaded(block: Document.() -> Unit): Unit { private fun whenDocumentLoaded(block: Document.() -> Unit): Unit {
if (document.body != null) { if (document.body != null) {
block(document) block(document)
@ -293,7 +275,7 @@ private fun whenDocumentLoaded(block: Document.() -> Unit): Unit {
/** /**
* Fetch and render visions for all elements with [VisionTagConsumer.OUTPUT_CLASS] class inside given [element]. * Fetch and render visions for all elements with [VisionTagConsumer.OUTPUT_CLASS] class inside given [element].
*/ */
public fun VisionClient.renderAllVisionsIn(element: Element) { public fun JsVisionClient.renderAllVisionsIn(element: Element) {
val elements = element.getElementsByClassName(VisionTagConsumer.OUTPUT_CLASS) val elements = element.getElementsByClassName(VisionTagConsumer.OUTPUT_CLASS)
logger.info { "Finished search for outputs. Found ${elements.length} items" } logger.info { "Finished search for outputs. Found ${elements.length} items" }
elements.asList().forEach { child -> elements.asList().forEach { child ->
@ -304,7 +286,7 @@ public fun VisionClient.renderAllVisionsIn(element: Element) {
/** /**
* Render all visions in an element with a given [id] * Render all visions in an element with a given [id]
*/ */
public fun VisionClient.renderAllVisionsById(document: Document, id: String): Unit { public fun JsVisionClient.renderAllVisionsById(document: Document, id: String): Unit {
val element = document.getElementById(id) val element = document.getElementById(id)
if (element != null) { if (element != null) {
renderAllVisionsIn(element) renderAllVisionsIn(element)
@ -317,13 +299,13 @@ public fun VisionClient.renderAllVisionsById(document: Document, id: String): Un
/** /**
* Fetch visions from the server for all elements with [VisionTagConsumer.OUTPUT_CLASS] class in the document body * Fetch visions from the server for all elements with [VisionTagConsumer.OUTPUT_CLASS] class in the document body
*/ */
public fun VisionClient.renderAllVisions(): Unit = whenDocumentLoaded { public fun JsVisionClient.renderAllVisions(): Unit = whenDocumentLoaded {
val element = body ?: error("Document does not have a body") val element = body ?: error("Document does not have a body")
renderAllVisionsIn(element) renderAllVisionsIn(element)
} }
public class VisionClientApplication(public val context: Context) : Application { public class VisionClientApplication(public val context: Context) : Application {
private val client = context.request(VisionClient) private val client = context.request(JsVisionClient)
override fun start(document: Document, state: Map<String, Any>) { override fun start(document: Document, state: Map<String, Any>) {
context.logger.info { context.logger.info {
@ -347,7 +329,7 @@ public fun runVisionClient(contextBuilder: ContextBuilder.() -> Unit) {
Global.logger.info { "Starting VisionForge context" } Global.logger.info { "Starting VisionForge context" }
val context = Context("VisionForge") { val context = Context("VisionForge") {
plugin(VisionClient) plugin(JsVisionClient)
contextBuilder() contextBuilder()
} }

View File

@ -20,7 +20,7 @@ import space.kscience.visionforge.html.VisionOfNumberField
import space.kscience.visionforge.html.VisionOfTextField import space.kscience.visionforge.html.VisionOfTextField
internal fun textVisionRenderer( internal fun textVisionRenderer(
client: VisionClient, client: JsVisionClient,
): ElementVisionRenderer = ElementVisionRenderer<VisionOfTextField> { name, vision, _ -> ): ElementVisionRenderer = ElementVisionRenderer<VisionOfTextField> { name, vision, _ ->
val fieldName = vision.name ?: "input[${vision.hashCode().toUInt()}]" val fieldName = vision.name ?: "input[${vision.hashCode().toUInt()}]"
vision.label?.let { vision.label?.let {
@ -42,7 +42,7 @@ internal fun textVisionRenderer(
} }
internal fun numberVisionRenderer( internal fun numberVisionRenderer(
client: VisionClient, client: JsVisionClient,
): ElementVisionRenderer = ElementVisionRenderer<VisionOfNumberField> { name, vision, _ -> ): ElementVisionRenderer = ElementVisionRenderer<VisionOfNumberField> { name, vision, _ ->
val fieldName = vision.name ?: "input[${vision.hashCode().toUInt()}]" val fieldName = vision.name ?: "input[${vision.hashCode().toUInt()}]"
vision.label?.let { vision.label?.let {
@ -87,7 +87,7 @@ internal fun FormData.toMeta(): Meta {
} }
internal fun formVisionRenderer( internal fun formVisionRenderer(
client: VisionClient, client: JsVisionClient,
): ElementVisionRenderer = ElementVisionRenderer<VisionOfHtmlForm> { name, vision, _ -> ): ElementVisionRenderer = ElementVisionRenderer<VisionOfHtmlForm> { name, vision, _ ->
val form = document.getElementById(vision.formId) as? HTMLFormElement val form = document.getElementById(vision.formId) as? HTMLFormElement

View File

@ -9,7 +9,7 @@ kscience {
} }
dependencies { dependencies {
api(projects.visionforgeSolid) api(projects.visionforgeSolid)
api("space.kscience:gdml:0.4.0") api("space.kscience:gdml:0.5.0")
} }
dependencies(jvmTest) { dependencies(jvmTest) {
implementation(spclibs.logback.classic) implementation(spclibs.logback.classic)

View File

@ -8,14 +8,14 @@ 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.visionforge.VisionClient import space.kscience.visionforge.JsVisionClient
import space.kscience.visionforge.renderAllVisions import space.kscience.visionforge.renderAllVisions
import space.kscience.visionforge.renderAllVisionsById import space.kscience.visionforge.renderAllVisionsById
import space.kscience.visionforge.renderAllVisionsIn import space.kscience.visionforge.renderAllVisionsIn
@JsExport @JsExport
public class VFNotebookClient : AbstractPlugin() { public class VFNotebookClient : AbstractPlugin() {
private val client by require(VisionClient) private val client by require(JsVisionClient)
public fun renderAllVisionsIn(element: Element) { public fun renderAllVisionsIn(element: Element) {
client.renderAllVisionsIn(element) client.renderAllVisionsIn(element)

View File

@ -2,7 +2,7 @@ plugins {
id("space.kscience.gradle.mpp") id("space.kscience.gradle.mpp")
} }
val markdownVersion = "0.4.1" val markdownVersion = "0.5.2"
kscience { kscience {
jvm() jvm()

View File

@ -18,7 +18,7 @@ import space.kscience.visionforge.markup.VisionOfMarkup.Companion.COMMONMARK_FOR
import space.kscience.visionforge.markup.VisionOfMarkup.Companion.GFM_FORMAT import space.kscience.visionforge.markup.VisionOfMarkup.Companion.GFM_FORMAT
public actual class MarkupPlugin : VisionPlugin(), ElementVisionRenderer { public actual class MarkupPlugin : VisionPlugin(), ElementVisionRenderer {
public val visionClient: VisionClient by require(VisionClient) public val visionClient: JsVisionClient by require(JsVisionClient)
override val tag: PluginTag get() = Companion.tag override val tag: PluginTag get() = Companion.tag
override val visionSerializersModule: SerializersModule get() = markupSerializersModule override val visionSerializersModule: SerializersModule get() = markupSerializersModule

View File

@ -2,7 +2,7 @@ plugins {
id("space.kscience.gradle.mpp") id("space.kscience.gradle.mpp")
} }
val plotlyVersion = "0.5.3" val plotlyVersion = "0.6.0"
kscience { kscience {
jvm() jvm()

View File

@ -11,12 +11,12 @@ import space.kscience.dataforge.names.asName
import space.kscience.plotly.PlotlyConfig import space.kscience.plotly.PlotlyConfig
import space.kscience.plotly.plot import space.kscience.plotly.plot
import space.kscience.visionforge.ElementVisionRenderer import space.kscience.visionforge.ElementVisionRenderer
import space.kscience.visionforge.JsVisionClient
import space.kscience.visionforge.Vision import space.kscience.visionforge.Vision
import space.kscience.visionforge.VisionClient
import space.kscience.visionforge.VisionPlugin import space.kscience.visionforge.VisionPlugin
public actual class PlotlyPlugin : VisionPlugin(), ElementVisionRenderer { public actual class PlotlyPlugin : VisionPlugin(), ElementVisionRenderer {
public val visionClient: VisionClient by require(VisionClient) public val visionClient: JsVisionClient by require(JsVisionClient)
override val tag: PluginTag get() = Companion.tag override val tag: PluginTag get() = Companion.tag

View File

@ -75,8 +75,8 @@ public class VisionRoute(
public fun Application.serveVisionData( public fun Application.serveVisionData(
configuration: VisionRoute, configuration: VisionRoute,
onEvent: suspend Vision.(VisionEvent) -> Unit = { event -> onEvent: suspend Vision.(VisionEvent) -> Unit = { event ->
if (event is VisionChange) { if (event is VisionChangeEvent) {
update(event) update(event.change)
} }
}, },
resolveVision: (Name) -> Vision?, resolveVision: (Name) -> Vision?,

View File

@ -15,6 +15,9 @@ public enum class CompositeType {
SUBTRACT SUBTRACT
} }
/**
* A CSG-based composite solid
*/
@Serializable @Serializable
@SerialName("solid.composite") @SerialName("solid.composite")
public class Composite( public class Composite(

View File

@ -5,6 +5,9 @@ import kotlinx.serialization.Serializable
import space.kscience.visionforge.MutableVisionContainer import space.kscience.visionforge.MutableVisionContainer
import space.kscience.visionforge.setChild import space.kscience.visionforge.setChild
/**
* A convex hull shape
*/
@Serializable @Serializable
@SerialName("solid.convex") @SerialName("solid.convex")
public class Convex(public val points: List<Float32Vector3D>) : SolidBase<Convex>() public class Convex(public val points: List<Float32Vector3D>) : SolidBase<Convex>()

View File

@ -12,7 +12,7 @@ import space.kscience.dataforge.names.asName
import space.kscience.visionforge.* import space.kscience.visionforge.*
@Serializable @Serializable
public abstract class LightSource : SolidBase<LightSource>() { public abstract class LightSource : MiscSolid() {
override val descriptor: MetaDescriptor get() = LightSource.descriptor override val descriptor: MetaDescriptor get() = LightSource.descriptor
public val color: ColorAccessor by colorProperty(SolidMaterial.COLOR_KEY) public val color: ColorAccessor by colorProperty(SolidMaterial.COLOR_KEY)

View File

@ -6,13 +6,19 @@ import space.kscience.visionforge.MutableVisionContainer
import space.kscience.visionforge.VisionBuilder import space.kscience.visionforge.VisionBuilder
import space.kscience.visionforge.setChild import space.kscience.visionforge.setChild
/**
* Utility solids
*/
public abstract class MiscSolid: SolidBase<MiscSolid>() public abstract class MiscSolid: SolidBase<MiscSolid>()
/**
* X-Y-Z axes
*/
@Serializable @Serializable
@SerialName("solid.axes") @SerialName("solid.axes")
public class AxesSolid(public val size: Double): MiscSolid(){ public class AxesSolid(public val size: Double): MiscSolid(){
public companion object{ public companion object{
public const val AXES_NAME: String = "@xes" public const val AXES_NAME: String = "@axes"
} }
} }

View File

@ -7,6 +7,9 @@ import kotlin.math.sin
public typealias Shape2D = List<Float32Vector2D> public typealias Shape2D = List<Float32Vector2D>
/**
* A builder for 2D shapes
*/
@Serializable @Serializable
public class Shape2DBuilder(private val points: ArrayList<Float32Vector2D> = ArrayList()) { public class Shape2DBuilder(private val points: ArrayList<Float32Vector2D> = ArrayList()) {

View File

@ -152,7 +152,10 @@ public var Vision.ignore: Boolean?
// get() = getProperty(SELECTED_KEY).boolean // get() = getProperty(SELECTED_KEY).boolean
// set(value) = setProperty(SELECTED_KEY, value) // set(value) = setProperty(SELECTED_KEY, value)
internal fun float(name: Name, default: Number): ReadWriteProperty<Solid, Number> = /**
*A [Float] solid property delegate
*/
internal fun float32(name: Name, default: Number): ReadWriteProperty<Solid, Number> =
object : ReadWriteProperty<Solid, Number> { object : ReadWriteProperty<Solid, Number> {
override fun getValue(thisRef: Solid, property: KProperty<*>): Number { override fun getValue(thisRef: Solid, property: KProperty<*>): Number {
return thisRef.properties.getValue(name)?.number ?: default return thisRef.properties.getValue(name)?.number ?: default
@ -163,7 +166,10 @@ internal fun float(name: Name, default: Number): ReadWriteProperty<Solid, Number
} }
} }
internal fun point( /**
* A [Float32Vector3D] solid property delegate
*/
internal fun float32Vector(
name: Name, name: Name,
defaultX: Float, defaultX: Float,
defaultY: Float = defaultX, defaultY: Float = defaultX,
@ -193,17 +199,17 @@ internal fun point(
} }
} }
public var Solid.position: Float32Vector3D? by point(POSITION_KEY, 0f) public var Solid.position: Float32Vector3D? by float32Vector(POSITION_KEY, 0f)
public var Solid.rotation: Float32Vector3D? by point(ROTATION_KEY, 0f) public var Solid.rotation: Float32Vector3D? by float32Vector(ROTATION_KEY, 0f)
public var Solid.scale: Float32Vector3D? by point(SCALE_KEY, 1f) public var Solid.scale: Float32Vector3D? by float32Vector(SCALE_KEY, 1f)
public var Solid.x: Number by float(X_POSITION_KEY, 0f) public var Solid.x: Number by float32(X_POSITION_KEY, 0f)
public var Solid.y: Number by float(Y_POSITION_KEY, 0f) public var Solid.y: Number by float32(Y_POSITION_KEY, 0f)
public var Solid.z: Number by float(Z_POSITION_KEY, 0f) public var Solid.z: Number by float32(Z_POSITION_KEY, 0f)
public var Solid.rotationX: Number by float(X_ROTATION_KEY, 0f) public var Solid.rotationX: Number by float32(X_ROTATION_KEY, 0f)
public var Solid.rotationY: Number by float(Y_ROTATION_KEY, 0f) public var Solid.rotationY: Number by float32(Y_ROTATION_KEY, 0f)
public var Solid.rotationZ: Number by float(Z_ROTATION_KEY, 0f) public var Solid.rotationZ: Number by float32(Z_ROTATION_KEY, 0f)
/** /**
* Raw quaternion value defined in properties * Raw quaternion value defined in properties
@ -241,9 +247,9 @@ public var Solid.quaternion: Quaternion
quaternionOrNull = value quaternionOrNull = value
} }
public var Solid.scaleX: Number by float(X_SCALE_KEY, 1f) public var Solid.scaleX: Number by float32(X_SCALE_KEY, 1f)
public var Solid.scaleY: Number by float(Y_SCALE_KEY, 1f) public var Solid.scaleY: Number by float32(Y_SCALE_KEY, 1f)
public var Solid.scaleZ: Number by float(Z_SCALE_KEY, 1f) public var Solid.scaleZ: Number by float32(Z_SCALE_KEY, 1f)
/** /**
* Add rotation with given [angle] relative to given [axis] * Add rotation with given [angle] relative to given [axis]

View File

@ -5,6 +5,9 @@ import kotlinx.serialization.Serializable
import space.kscience.dataforge.meta.descriptors.MetaDescriptor import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.visionforge.AbstractVision import space.kscience.visionforge.AbstractVision
/**
* An abstract solid that is both [Solid] and [AbstractVision]
*/
@Serializable @Serializable
@SerialName("solid") @SerialName("solid")
public open class SolidBase<T : Solid> : AbstractVision(), Solid { public open class SolidBase<T : Solid> : AbstractVision(), Solid {

View File

@ -12,6 +12,9 @@ import space.kscience.visionforge.solid.SolidMaterial.Companion.MATERIAL_COLOR_K
import space.kscience.visionforge.solid.SolidMaterial.Companion.MATERIAL_KEY import space.kscience.visionforge.solid.SolidMaterial.Companion.MATERIAL_KEY
import space.kscience.visionforge.solid.SolidMaterial.Companion.MATERIAL_OPACITY_KEY import space.kscience.visionforge.solid.SolidMaterial.Companion.MATERIAL_OPACITY_KEY
/**
* A scheme for vision material
*/
@VisionBuilder @VisionBuilder
public class SolidMaterial : Scheme() { public class SolidMaterial : Scheme() {

View File

@ -21,6 +21,7 @@ import space.kscience.visionforge.solid.SolidReference.Companion.REFERENCE_CHILD
* Get a vision prototype if it is a [SolidReference] or vision itself if it is not. * Get a vision prototype if it is a [SolidReference] or vision itself if it is not.
* Unref is recursive, so it always returns a non-reference. * Unref is recursive, so it always returns a non-reference.
*/ */
@Suppress("RecursivePropertyAccessor")
public val Vision.prototype: Solid public val Vision.prototype: Solid
get() = when (this) { get() = when (this) {
is SolidReference -> prototype.prototype is SolidReference -> prototype.prototype
@ -239,95 +240,4 @@ public fun SolidGroup.newRef(
error("Can't add different prototype on top of existing one") error("Can't add different prototype on top of existing one")
} }
return children.ref(prototypeName, name?.parseAsName()) return children.ref(prototypeName, name?.parseAsName())
} }
//
//
///**
// * A reference [Solid] to reuse a template object
// */
//@Serializable
//@SerialName("solid.ref")
//public class SolidReferenceGroup(
// public val refName: Name,
//) : VisionGroup(), SolidReference, VisionGroup<Solid>, Solid {
//
// /**
// * Recursively search for defined template in the parent
// */
// override val prototype: Solid by lazy {
// if (parent == null) error("No parent is present for SolidReferenceGroup")
// if (parent !is PrototypeHolder) error("Parent does not hold prototypes")
// (parent as? PrototypeHolder)?.getPrototype(refName) ?: error("Prototype with name $refName not found")
// }
//
// override val items: Map<NameToken, VisionGroupItem<Solid>>
// get() = (prototype as? VisionGroup<*>)?.items
// ?.filter { it.key != SolidGroup.PROTOTYPES_TOKEN }
// ?.mapValues {
// VisionGroupItem.Node(ReferenceChild(this, it.key.asName()))
// } ?: emptyMap()
//
// override val descriptor: MetaDescriptor get() = prototype.descriptor
//
//
// /**
// * A ProxyChild is created temporarily only to interact with properties, it does not store any values
// * (properties are stored in external cache) and created and destroyed on-demand).
// */
// private class ReferenceChild(
// val owner: SolidReferenceGroup,
// private val refName: Name,
// ) : SolidReference, VisionGroup<Solid>, Solid {
//
// override val prototype: Solid by lazy {
// if (refName.isEmpty()) {
// owner.prototype
// } else {
// val proto = (owner.prototype).children.get(refName)
// ?: error("Prototype with name $refName not found in SolidReferenceGroup ${owner.refName}")
// proto as? Solid ?: error("Prototype with name $refName is ${proto::class} but expected Solid")
//// proto.unref as? Solid
//// ?: error("Prototype with name $refName is ${proto::class} but expected Solid")
// }
// }
//
// override val meta: ObservableMutableMeta by lazy {
// owner.meta.getOrCreate(childToken(refName).asName())
// }
//
// override val items: Map<NameToken, VisionGroupItem<Solid>>
// get() = (prototype as? VisionGroup<*>)?.items
// ?.filter { it.key != SolidGroup.PROTOTYPES_TOKEN }
// ?.mapValues { (key, _) ->
// VisionGroupItem.Node(ReferenceChild(owner, refName + key.asName()))
// } ?: emptyMap()
//
// override var parent: VisionGroup<*>?
// get() {
// val parentName = refName.cutLast()
// return if (parentName.isEmpty()) owner else ReferenceChild(owner, parentName)
// }
// set(_) {
// error("Setting a parent for a reference child is not possible")
// }
//
// override fun invalidateProperty(propertyName: Name) {
// owner.invalidateProperty(childPropertyName(refName, propertyName))
// }
//
// override fun update(change: VisionChange) {
// change.properties?.let {
// updateProperties(it, Name.EMPTY)
// }
// }
//
// override val descriptor: MetaDescriptor get() = prototype.descriptor
//
// }
//
// public companion object {
// public const val REFERENCE_CHILD_PROPERTY_PREFIX: String = "@child"
// }
//}

View File

@ -12,21 +12,21 @@ import space.kscience.dataforge.meta.toDynamic
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
import space.kscience.visionforge.JsVisionClient
import space.kscience.visionforge.Vision import space.kscience.visionforge.Vision
import space.kscience.visionforge.VisionClient
import tabulator.Tabulator import tabulator.Tabulator
import tabulator.TabulatorFull import tabulator.TabulatorFull
public class TableVisionJsPlugin : AbstractPlugin(), ElementVisionRenderer { public class TableVisionJsPlugin : AbstractPlugin(), ElementVisionRenderer {
public val visionClient: VisionClient by require(VisionClient) public val visionClient: JsVisionClient by require(JsVisionClient)
public val tablesBase: TableVisionPlugin by require(TableVisionPlugin) public val tablesBase: TableVisionPlugin by require(TableVisionPlugin)
override val tag: PluginTag get() = Companion.tag override val tag: PluginTag get() = Companion.tag
override fun attach(context: Context) { override fun attach(context: Context) {
super.attach(context) super.attach(context)
kotlinext.js.require("tabulator-tables/dist/css/tabulator.min.css") kotlinext.js.require<Any>("tabulator-tables/dist/css/tabulator.min.css")
kotlinext.js.require("tabulator-tables/src/js/modules/ResizeColumns/ResizeColumns.js") kotlinext.js.require<Any>("tabulator-tables/src/js/modules/ResizeColumns/ResizeColumns.js")
} }
override fun rateVision(vision: Vision): Int = when (vision) { override fun rateVision(vision: Vision): Int = when (vision) {

View File

@ -1,17 +1,23 @@
plugins { plugins {
id("space.kscience.gradle.js") id("space.kscience.gradle.mpp")
} }
kotlin{ kotlin{
explicitApi = org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode.Disabled explicitApi = org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode.Disabled
}
kscience{
js{ js{
binaries.library() 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"))
}
}
} }
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"))
}

View File

@ -123,8 +123,8 @@ public class ThreeCanvas(
element.appendChild(canvas) element.appendChild(canvas)
updateSize() updateSize()
canvas.addEventListener("pointerdown", { canvas.addEventListener("pointerdown", {
val picked = pick() val newPicked = pick()
options.onSelect?.invoke(picked?.fullName()) options.onSelect?.invoke(newPicked?.fullName())
}, false) }, false)
//Attach listener to track mouse changes //Attach listener to track mouse changes
@ -143,12 +143,12 @@ public class ThreeCanvas(
addControls(canvas, options.controls) addControls(canvas, options.controls)
renderer.setAnimationLoop { renderer.setAnimationLoop {
val picked = pick() val newPicked = pick()
if (picked != null && this.picked != picked) { if (newPicked != null && picked !== newPicked) {
this.picked?.toggleHighlight(false, HIGHLIGHT_NAME, HIGHLIGHT_MATERIAL) picked?.toggleHighlight(false, HIGHLIGHT_NAME, HIGHLIGHT_MATERIAL)
picked.toggleHighlight(true, HIGHLIGHT_NAME, HIGHLIGHT_MATERIAL) newPicked.toggleHighlight(true, HIGHLIGHT_NAME, HIGHLIGHT_MATERIAL)
this.picked = picked picked = newPicked
} }
renderer.render(scene, camera) renderer.render(scene, camera)
@ -326,7 +326,7 @@ public class ThreeCanvas(
public val HIGHLIGHT_MATERIAL: MeshLineMaterial = MeshLineMaterial().apply { public val HIGHLIGHT_MATERIAL: MeshLineMaterial = MeshLineMaterial().apply {
color.set(Colors.blue) color.set(Colors.blue)
thickness = 2f thickness = 1f
cached = true cached = true
} }
// //

View File

@ -7,12 +7,10 @@ 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.ElementVisionRenderer import space.kscience.visionforge.*
import space.kscience.visionforge.Vision
import space.kscience.visionforge.VisionChildren
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.visible import space.kscience.visionforge.solid.three.set
import three.core.Object3D import three.core.Object3D
import kotlin.collections.set import kotlin.collections.set
import kotlin.reflect.KClass import kotlin.reflect.KClass
@ -23,6 +21,8 @@ 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>()
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)
@ -90,6 +90,8 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
val child = vision.children.getChild(childName) val child = vision.children.getChild(childName)
logger.debug { "Changing vision $childName to $child" }
//removing old object //removing old object
findChild(childName)?.let { oldChild -> findChild(childName)?.let { oldChild ->
oldChild.parent?.remove(oldChild) oldChild.parent?.remove(oldChild)

Some files were not shown because too many files have changed in this diff Show More