0.2.0 #71
@ -29,7 +29,10 @@ kotlin {
|
|||||||
jvm {
|
jvm {
|
||||||
withJava()
|
withJava()
|
||||||
compilations.all {
|
compilations.all {
|
||||||
kotlinOptions.jvmTarget = "11"
|
kotlinOptions{
|
||||||
|
jvmTarget = "11"
|
||||||
|
freeCompilerArgs = freeCompilerArgs + "-Xjvm-default=all" + "-Xopt-in=kotlin.RequiresOptIn" + "-Xlambdas=indy"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
testRuns["test"].executionTask.configure {
|
testRuns["test"].executionTask.configure {
|
||||||
useJUnitPlatform()
|
useJUnitPlatform()
|
||||||
@ -51,33 +54,34 @@ kotlin {
|
|||||||
sourceSets {
|
sourceSets {
|
||||||
val commonMain by getting {
|
val commonMain by getting {
|
||||||
dependencies {
|
dependencies {
|
||||||
api(project(":visionforge-solid"))
|
implementation(projects.visionforgeSolid)
|
||||||
api(project(":visionforge-gdml"))
|
implementation(projects.visionforgeGdml)
|
||||||
api(project(":visionforge-plotly"))
|
implementation(projects.visionforgePlotly)
|
||||||
api(projects.visionforge.visionforgeMarkdown)
|
implementation(projects.visionforgeMarkdown)
|
||||||
api(projects.visionforge.cernRootLoader)
|
implementation(projects.cernRootLoader)
|
||||||
|
implementation(projects.jupyter.jupyterBase)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val jsMain by getting {
|
val jsMain by getting {
|
||||||
dependencies {
|
dependencies {
|
||||||
api(project(":ui:ring"))
|
implementation(projects.ui.ring)
|
||||||
api(project(":visionforge-threejs"))
|
implementation(projects.visionforgeThreejs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val jvmMain by getting {
|
val jvmMain by getting {
|
||||||
dependencies {
|
dependencies {
|
||||||
api(project(":visionforge-server"))
|
implementation(projects.visionforgeServer)
|
||||||
api("ch.qos.logback:logback-classic:1.2.3")
|
implementation("ch.qos.logback:logback-classic:1.2.3")
|
||||||
api("com.github.Ricky12Awesome:json-schema-serialization:0.6.6")
|
implementation("com.github.Ricky12Awesome:json-schema-serialization:0.6.6")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType<org.jetbrains.kotlinx.jupyter.api.plugin.tasks.JupyterApiResourcesTask> {
|
val processJupyterApiResources by tasks.getting(org.jetbrains.kotlinx.jupyter.api.plugin.tasks.JupyterApiResourcesTask::class){
|
||||||
libraryProducers = listOf("space.kscience.visionforge.examples.VisionForgePlayGroundForJupyter")
|
libraryProducers = listOf("space.kscience.visionforge.examples.VisionForgePlayGroundForJupyter")
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.findByName("shadowJar")?.dependsOn("processJupyterApiResources")
|
tasks.findByName("shadowJar")?.dependsOn(processJupyterApiResources)
|
@ -1,39 +1,25 @@
|
|||||||
package space.kscience.visionforge.examples
|
package space.kscience.visionforge.examples
|
||||||
|
|
||||||
import kotlinx.html.stream.createHTML
|
|
||||||
import org.jetbrains.kotlinx.jupyter.api.HTML
|
|
||||||
import org.jetbrains.kotlinx.jupyter.api.libraries.JupyterIntegration
|
|
||||||
import org.jetbrains.kotlinx.jupyter.api.libraries.resources
|
import org.jetbrains.kotlinx.jupyter.api.libraries.resources
|
||||||
import space.kscience.dataforge.context.Context
|
import space.kscience.dataforge.context.Context
|
||||||
import space.kscience.dataforge.misc.DFExperimental
|
import space.kscience.dataforge.misc.DFExperimental
|
||||||
import space.kscience.gdml.Gdml
|
import space.kscience.gdml.Gdml
|
||||||
import space.kscience.plotly.Plot
|
import space.kscience.plotly.Plot
|
||||||
import space.kscience.visionforge.Vision
|
|
||||||
import space.kscience.visionforge.gdml.toVision
|
import space.kscience.visionforge.gdml.toVision
|
||||||
import space.kscience.visionforge.html.HtmlVisionFragment
|
import space.kscience.visionforge.jupyter.JupyterPluginBase
|
||||||
import space.kscience.visionforge.html.Page
|
|
||||||
import space.kscience.visionforge.html.embedAndRenderVisionFragment
|
|
||||||
import space.kscience.visionforge.plotly.PlotlyPlugin
|
import space.kscience.visionforge.plotly.PlotlyPlugin
|
||||||
import space.kscience.visionforge.plotly.asVision
|
import space.kscience.visionforge.plotly.asVision
|
||||||
import space.kscience.visionforge.solid.Solids
|
import space.kscience.visionforge.solid.Solids
|
||||||
import space.kscience.visionforge.visionManager
|
|
||||||
|
|
||||||
@DFExperimental
|
@DFExperimental
|
||||||
public class VisionForgePlayGroundForJupyter : JupyterIntegration() {
|
internal class VisionForgePlayGroundForJupyter : JupyterPluginBase(
|
||||||
|
Context("VisionForge") {
|
||||||
private val context = Context("VisionForge") {
|
|
||||||
plugin(Solids)
|
plugin(Solids)
|
||||||
plugin(PlotlyPlugin)
|
plugin(PlotlyPlugin)
|
||||||
}
|
}
|
||||||
|
) {
|
||||||
|
|
||||||
private var counter = 0
|
override fun Builder.afterLoaded() {
|
||||||
|
|
||||||
private fun produceHtmlVisionString(fragment: HtmlVisionFragment) = createHTML().apply {
|
|
||||||
embedAndRenderVisionFragment(context.visionManager, counter++, fragment = fragment)
|
|
||||||
}.finalize()
|
|
||||||
|
|
||||||
override fun Builder.onLoaded() {
|
|
||||||
|
|
||||||
resources {
|
resources {
|
||||||
js("VisionForge") {
|
js("VisionForge") {
|
||||||
classPath("js/visionforge-playground.js")
|
classPath("js/visionforge-playground.js")
|
||||||
@ -44,41 +30,20 @@ public class VisionForgePlayGroundForJupyter : JupyterIntegration() {
|
|||||||
"space.kscience.gdml.*",
|
"space.kscience.gdml.*",
|
||||||
"space.kscience.plotly.*",
|
"space.kscience.plotly.*",
|
||||||
"space.kscience.plotly.models.*",
|
"space.kscience.plotly.models.*",
|
||||||
"kotlinx.html.*",
|
|
||||||
"space.kscience.visionforge.solid.*",
|
"space.kscience.visionforge.solid.*",
|
||||||
"space.kscience.visionforge.html.Page",
|
|
||||||
"space.kscience.visionforge.html.page"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
render<Gdml> { gdmlModel ->
|
render<Gdml> { gdmlModel ->
|
||||||
val fragment = HtmlVisionFragment {
|
handler.produceHtml {
|
||||||
vision(gdmlModel.toVision())
|
vision(gdmlModel.toVision())
|
||||||
}
|
}
|
||||||
HTML(produceHtmlVisionString(fragment))
|
|
||||||
}
|
|
||||||
|
|
||||||
render<Vision> { vision ->
|
|
||||||
val fragment = HtmlVisionFragment {
|
|
||||||
vision(vision)
|
|
||||||
}
|
|
||||||
|
|
||||||
HTML(produceHtmlVisionString(fragment))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render<Plot> { plot ->
|
render<Plot> { plot ->
|
||||||
val fragment = HtmlVisionFragment {
|
handler.produceHtml {
|
||||||
vision(plot.asVision())
|
vision(plot.asVision())
|
||||||
}
|
}
|
||||||
|
|
||||||
HTML(produceHtmlVisionString(fragment))
|
|
||||||
}
|
|
||||||
|
|
||||||
render<space.kscience.plotly.PlotlyHtmlFragment> { fragment ->
|
|
||||||
HTML(createHTML().apply(fragment.visit).finalize())
|
|
||||||
}
|
|
||||||
|
|
||||||
render<Page> { page ->
|
|
||||||
HTML(page.render(createHTML()), true)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import space.kscience.dataforge.context.Global
|
|||||||
import space.kscience.dataforge.context.fetch
|
import space.kscience.dataforge.context.fetch
|
||||||
import space.kscience.dataforge.names.asName
|
import space.kscience.dataforge.names.asName
|
||||||
import space.kscience.visionforge.VisionManager
|
import space.kscience.visionforge.VisionManager
|
||||||
import space.kscience.visionforge.html.visionOfForm
|
import space.kscience.visionforge.html.formFragment
|
||||||
import space.kscience.visionforge.onPropertyChange
|
import space.kscience.visionforge.onPropertyChange
|
||||||
import space.kscience.visionforge.three.server.close
|
import space.kscience.visionforge.three.server.close
|
||||||
import space.kscience.visionforge.three.server.openInBrowser
|
import space.kscience.visionforge.three.server.openInBrowser
|
||||||
@ -18,7 +18,7 @@ fun main() {
|
|||||||
val server = visionManager.serve {
|
val server = visionManager.serve {
|
||||||
useScript("js/visionforge-playground.js")
|
useScript("js/visionforge-playground.js")
|
||||||
page {
|
page {
|
||||||
val form = visionOfForm("form") {
|
val form = formFragment("form") {
|
||||||
label {
|
label {
|
||||||
htmlFor = "fname"
|
htmlFor = "fname"
|
||||||
+"First name:"
|
+"First name:"
|
||||||
|
@ -9,6 +9,7 @@ import space.kscience.visionforge.html.scriptHeader
|
|||||||
import space.kscience.visionforge.makeFile
|
import space.kscience.visionforge.makeFile
|
||||||
import space.kscience.visionforge.three.server.VisionServer
|
import space.kscience.visionforge.three.server.VisionServer
|
||||||
import space.kscience.visionforge.three.server.useScript
|
import space.kscience.visionforge.three.server.useScript
|
||||||
|
import space.kscience.visionforge.visionManager
|
||||||
import java.awt.Desktop
|
import java.awt.Desktop
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
|
||||||
@ -25,7 +26,7 @@ public fun Context.makeVisionFile(
|
|||||||
show: Boolean = true,
|
show: Boolean = true,
|
||||||
content: VisionTagConsumer<*>.() -> Unit
|
content: VisionTagConsumer<*>.() -> Unit
|
||||||
): Unit {
|
): Unit {
|
||||||
val actualPath = page(title, content = content).makeFile(path) { actualPath ->
|
val actualPath = visionManager.page(title, content = content).makeFile(path) { actualPath ->
|
||||||
mapOf("playground" to scriptHeader("js/visionforge-playground.js", resourceLocation, actualPath))
|
mapOf("playground" to scriptHeader("js/visionforge-playground.js", resourceLocation, actualPath))
|
||||||
}
|
}
|
||||||
if (show) Desktop.getDesktop().browse(actualPath.toFile().toURI())
|
if (show) Desktop.getDesktop().browse(actualPath.toFile().toURI())
|
||||||
|
@ -9,12 +9,12 @@ kotlin {
|
|||||||
sourceSets {
|
sourceSets {
|
||||||
commonMain{
|
commonMain{
|
||||||
dependencies{
|
dependencies{
|
||||||
api(projects.visionforge.visionforgeCore)
|
api(projects.visionforgeCore)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
jvmMain {
|
jvmMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":visionforge-server"))
|
api(projects.visionforgeServer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
62
jupyter/jupyter-base/src/jvmMain/kotlin/JupyterPluginBase.kt
Normal file
62
jupyter/jupyter-base/src/jvmMain/kotlin/JupyterPluginBase.kt
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
package space.kscience.visionforge.jupyter
|
||||||
|
|
||||||
|
import kotlinx.html.stream.createHTML
|
||||||
|
import org.jetbrains.kotlinx.jupyter.api.HTML
|
||||||
|
import org.jetbrains.kotlinx.jupyter.api.declare
|
||||||
|
import org.jetbrains.kotlinx.jupyter.api.libraries.JupyterIntegration
|
||||||
|
import space.kscience.dataforge.context.Context
|
||||||
|
import space.kscience.dataforge.context.ContextAware
|
||||||
|
import space.kscience.dataforge.misc.DFExperimental
|
||||||
|
import space.kscience.visionforge.Vision
|
||||||
|
import space.kscience.visionforge.html.HtmlFormFragment
|
||||||
|
import space.kscience.visionforge.html.HtmlVisionFragment
|
||||||
|
import space.kscience.visionforge.html.Page
|
||||||
|
import space.kscience.visionforge.html.fragment
|
||||||
|
|
||||||
|
@DFExperimental
|
||||||
|
public abstract class JupyterPluginBase(final override val context: Context) : JupyterIntegration(), ContextAware {
|
||||||
|
|
||||||
|
protected val handler: VisionForgeServerHandler = VisionForgeServerHandler(context)
|
||||||
|
|
||||||
|
protected abstract fun Builder.afterLoaded()
|
||||||
|
|
||||||
|
final override fun Builder.onLoaded() {
|
||||||
|
|
||||||
|
onLoaded {
|
||||||
|
declare("visionForge" to handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
onShutdown {
|
||||||
|
handler.stopServer()
|
||||||
|
}
|
||||||
|
|
||||||
|
import(
|
||||||
|
"kotlinx.html.*",
|
||||||
|
"space.kscience.visionforge.html.*"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
render<HtmlVisionFragment> { fragment ->
|
||||||
|
handler.produceHtml(fragment = fragment)
|
||||||
|
}
|
||||||
|
|
||||||
|
render<Vision> { vision ->
|
||||||
|
handler.produceHtml {
|
||||||
|
vision(vision)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
render<Page> { page ->
|
||||||
|
HTML(page.render(createHTML()), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
render<HtmlFormFragment> { fragment ->
|
||||||
|
handler.produceHtml {
|
||||||
|
fragment(fragment.formBody)
|
||||||
|
vision(fragment.vision)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
afterLoaded()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
package space.kscience.visionforge.jupyter
|
||||||
|
|
||||||
|
import io.ktor.server.engine.ApplicationEngine
|
||||||
|
import kotlinx.html.FORM
|
||||||
|
import kotlinx.html.p
|
||||||
|
import kotlinx.html.stream.createHTML
|
||||||
|
import kotlinx.html.style
|
||||||
|
import org.jetbrains.kotlinx.jupyter.api.HTML
|
||||||
|
import org.jetbrains.kotlinx.jupyter.api.MimeTypedResult
|
||||||
|
import space.kscience.dataforge.context.Context
|
||||||
|
import space.kscience.dataforge.context.ContextAware
|
||||||
|
import space.kscience.dataforge.context.info
|
||||||
|
import space.kscience.dataforge.context.logger
|
||||||
|
import space.kscience.dataforge.meta.get
|
||||||
|
import space.kscience.dataforge.meta.int
|
||||||
|
import space.kscience.dataforge.meta.string
|
||||||
|
import space.kscience.visionforge.html.HtmlFormFragment
|
||||||
|
import space.kscience.visionforge.html.HtmlFragment
|
||||||
|
import space.kscience.visionforge.html.HtmlVisionFragment
|
||||||
|
import space.kscience.visionforge.html.visionFragment
|
||||||
|
import space.kscience.visionforge.three.server.VisionServer
|
||||||
|
import space.kscience.visionforge.three.server.serve
|
||||||
|
import space.kscience.visionforge.visionManager
|
||||||
|
|
||||||
|
public class VisionForgeServerHandler(override val context: Context) : ContextAware {
|
||||||
|
private var counter = 0
|
||||||
|
|
||||||
|
private var engine: ApplicationEngine? = null
|
||||||
|
private var server: VisionServer? = null
|
||||||
|
|
||||||
|
public var isolateFragments: Boolean = false
|
||||||
|
|
||||||
|
public fun legacyMode() {
|
||||||
|
isolateFragments = true
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun startServer(
|
||||||
|
host: String = context.properties["visionforge.host"].string ?: "localhost",
|
||||||
|
port: Int = context.properties["visionforge.port"].int ?: VisionServer.DEFAULT_PORT,
|
||||||
|
configuration: VisionServer.() -> Unit = {},
|
||||||
|
): HtmlFragment {
|
||||||
|
engine?.stop(1000, 2000)
|
||||||
|
engine = context.visionManager.serve(host, port) {
|
||||||
|
configuration()
|
||||||
|
server = this
|
||||||
|
}.start()
|
||||||
|
return {
|
||||||
|
if(server!= null){
|
||||||
|
p {
|
||||||
|
style = "color: red;"
|
||||||
|
+"Stopping current VisionForge server"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
style = "color: blue;"
|
||||||
|
+"Starting VisionForge server on http://$host:$port"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun stopServer() {
|
||||||
|
engine?.apply {
|
||||||
|
logger.info { "Stopping VisionForge server" }
|
||||||
|
}?.stop(1000, 2000)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun produceHtmlString(
|
||||||
|
fragment: HtmlVisionFragment,
|
||||||
|
): String = server?.serveVisionsFromFragment("content[${counter++}]", fragment)
|
||||||
|
?: createHTML().apply {
|
||||||
|
visionFragment(context.visionManager, fragment = fragment)
|
||||||
|
}.finalize()
|
||||||
|
|
||||||
|
public fun produceHtml(isolated: Boolean? = null, fragment: HtmlVisionFragment): MimeTypedResult =
|
||||||
|
HTML(produceHtmlString(fragment), isolated ?: isolateFragments)
|
||||||
|
|
||||||
|
public fun fragment(body: HtmlVisionFragment): MimeTypedResult = produceHtml(fragment = body)
|
||||||
|
public fun page(body: HtmlVisionFragment): MimeTypedResult = produceHtml(true, body)
|
||||||
|
|
||||||
|
public fun form(builder: FORM.() -> Unit): HtmlFormFragment = HtmlFormFragment("form[${counter++}]", builder = builder)
|
||||||
|
}
|
@ -1,77 +0,0 @@
|
|||||||
package space.kscience.visionforge.jupyter
|
|
||||||
|
|
||||||
import io.ktor.server.cio.CIO
|
|
||||||
import io.ktor.server.engine.ApplicationEngine
|
|
||||||
import io.ktor.server.engine.embeddedServer
|
|
||||||
import kotlinx.html.stream.createHTML
|
|
||||||
import org.jetbrains.kotlinx.jupyter.api.HTML
|
|
||||||
import org.jetbrains.kotlinx.jupyter.api.libraries.JupyterIntegration
|
|
||||||
import space.kscience.dataforge.context.Context
|
|
||||||
import space.kscience.dataforge.context.ContextAware
|
|
||||||
import space.kscience.dataforge.meta.get
|
|
||||||
import space.kscience.dataforge.meta.int
|
|
||||||
import space.kscience.dataforge.meta.string
|
|
||||||
import space.kscience.dataforge.misc.DFExperimental
|
|
||||||
import space.kscience.visionforge.Vision
|
|
||||||
import space.kscience.visionforge.html.HtmlVisionFragment
|
|
||||||
import space.kscience.visionforge.html.Page
|
|
||||||
import space.kscience.visionforge.html.embedAndRenderVisionFragment
|
|
||||||
import space.kscience.visionforge.three.server.VisionServer
|
|
||||||
import space.kscience.visionforge.three.server.visionServer
|
|
||||||
import space.kscience.visionforge.visionManager
|
|
||||||
|
|
||||||
private const val DEFAULT_VISIONFORGE_PORT = 88898
|
|
||||||
|
|
||||||
@DFExperimental
|
|
||||||
public abstract class JupyterPluginBase(
|
|
||||||
override val context: Context,
|
|
||||||
) : JupyterIntegration(), ContextAware {
|
|
||||||
|
|
||||||
private var counter = 0
|
|
||||||
|
|
||||||
private fun produceHtmlVisionString(fragment: HtmlVisionFragment) = createHTML().apply {
|
|
||||||
embedAndRenderVisionFragment(context.visionManager, counter++, fragment = fragment)
|
|
||||||
}.finalize()
|
|
||||||
|
|
||||||
private var engine: ApplicationEngine? = null
|
|
||||||
private var server: VisionServer? = null
|
|
||||||
|
|
||||||
override fun Builder.onLoaded() {
|
|
||||||
|
|
||||||
onLoaded {
|
|
||||||
val host = context.properties["visionforge.host"].string ?: "localhost"
|
|
||||||
val port = context.properties["visionforge.port"].int ?: DEFAULT_VISIONFORGE_PORT
|
|
||||||
engine = context.embeddedServer(CIO, port, host) {
|
|
||||||
server = visionServer(context)
|
|
||||||
}.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
onShutdown {
|
|
||||||
engine?.stop(1000, 1000)
|
|
||||||
engine = null
|
|
||||||
server = null
|
|
||||||
}
|
|
||||||
|
|
||||||
import(
|
|
||||||
"kotlinx.html.*",
|
|
||||||
"space.kscience.visionforge.html.Page",
|
|
||||||
"space.kscience.visionforge.html.page",
|
|
||||||
)
|
|
||||||
|
|
||||||
render<Vision> { vision ->
|
|
||||||
val server = this@JupyterPluginBase.server
|
|
||||||
if (server == null) {
|
|
||||||
HTML(produceHtmlVisionString { vision(vision) })
|
|
||||||
} else {
|
|
||||||
val route = "route.${counter++}"
|
|
||||||
HTML(server.createHtmlAndServe(route,route, emptyList()){
|
|
||||||
vision(vision)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render<Page> { page ->
|
|
||||||
//HTML(page.render(createHTML()), true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -32,18 +32,19 @@ kotlin {
|
|||||||
sourceSets {
|
sourceSets {
|
||||||
commonMain {
|
commonMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
api(project(":visionforge-solid"))
|
implementation(projects.visionforgeSolid)
|
||||||
|
implementation(projects.jupyter.jupyterBase)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
jvmMain {
|
jvmMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":visionforge-gdml"))
|
implementation(projects.visionforgeGdml)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
jsMain {
|
jsMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
api(project(":visionforge-threejs"))
|
implementation(projects.visionforgeThreejs)
|
||||||
implementation(project(":ui:ring"))
|
implementation(projects.ui.ring)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,34 +1,21 @@
|
|||||||
package space.kscience.visionforge.gdml.jupyter
|
package space.kscience.visionforge.gdml.jupyter
|
||||||
|
|
||||||
import kotlinx.html.stream.createHTML
|
|
||||||
import org.jetbrains.kotlinx.jupyter.api.HTML
|
|
||||||
import org.jetbrains.kotlinx.jupyter.api.libraries.JupyterIntegration
|
|
||||||
import org.jetbrains.kotlinx.jupyter.api.libraries.resources
|
import org.jetbrains.kotlinx.jupyter.api.libraries.resources
|
||||||
import space.kscience.dataforge.context.Context
|
import space.kscience.dataforge.context.Context
|
||||||
import space.kscience.dataforge.misc.DFExperimental
|
import space.kscience.dataforge.misc.DFExperimental
|
||||||
import space.kscience.gdml.Gdml
|
import space.kscience.gdml.Gdml
|
||||||
import space.kscience.visionforge.Vision
|
|
||||||
import space.kscience.visionforge.gdml.toVision
|
import space.kscience.visionforge.gdml.toVision
|
||||||
import space.kscience.visionforge.html.HtmlVisionFragment
|
import space.kscience.visionforge.jupyter.JupyterPluginBase
|
||||||
import space.kscience.visionforge.html.Page
|
|
||||||
import space.kscience.visionforge.html.embedAndRenderVisionFragment
|
|
||||||
import space.kscience.visionforge.solid.Solids
|
import space.kscience.visionforge.solid.Solids
|
||||||
import space.kscience.visionforge.visionManager
|
|
||||||
|
|
||||||
@DFExperimental
|
@DFExperimental
|
||||||
internal class GdmlForJupyter : JupyterIntegration() {
|
internal class GdmlForJupyter : JupyterPluginBase(
|
||||||
|
Context("GDML") {
|
||||||
private val context = Context("GDML") {
|
|
||||||
plugin(Solids)
|
plugin(Solids)
|
||||||
}
|
}
|
||||||
|
) {
|
||||||
|
|
||||||
private var counter = 0
|
override fun Builder.afterLoaded() {
|
||||||
|
|
||||||
private fun produceHtmlVisionString(fragment: HtmlVisionFragment) = createHTML().apply {
|
|
||||||
embedAndRenderVisionFragment(context.visionManager, counter++, fragment = fragment)
|
|
||||||
}.finalize()
|
|
||||||
|
|
||||||
override fun Builder.onLoaded() {
|
|
||||||
|
|
||||||
resources {
|
resources {
|
||||||
js("three") {
|
js("three") {
|
||||||
@ -38,23 +25,11 @@ internal class GdmlForJupyter : JupyterIntegration() {
|
|||||||
|
|
||||||
import(
|
import(
|
||||||
"space.kscience.gdml.*",
|
"space.kscience.gdml.*",
|
||||||
"kotlinx.html.*",
|
|
||||||
"space.kscience.visionforge.solid.*",
|
|
||||||
"space.kscience.visionforge.html.Page",
|
|
||||||
"space.kscience.visionforge.html.page",
|
|
||||||
"space.kscience.visionforge.gdml.jupyter.*"
|
"space.kscience.visionforge.gdml.jupyter.*"
|
||||||
)
|
)
|
||||||
|
|
||||||
render<Vision> { vision ->
|
|
||||||
HTML(produceHtmlVisionString { vision(vision) })
|
|
||||||
}
|
|
||||||
|
|
||||||
render<Gdml> { gdmlModel ->
|
render<Gdml> { gdmlModel ->
|
||||||
HTML(produceHtmlVisionString { vision(gdmlModel.toVision()) })
|
handler.produceHtml { vision(gdmlModel.toVision()) }
|
||||||
}
|
|
||||||
|
|
||||||
render<Page> { page ->
|
|
||||||
HTML(page.render(createHTML()), true)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,6 +61,6 @@ include(
|
|||||||
":demo:playground",
|
":demo:playground",
|
||||||
":demo:plotly-fx",
|
":demo:plotly-fx",
|
||||||
":demo:js-playground",
|
":demo:js-playground",
|
||||||
":jupyter:visionforge-jupyter-base",
|
":jupyter:jupyter-base",
|
||||||
":jupyter:visionforge-jupyter-gdml"
|
":jupyter:visionforge-jupyter-gdml"
|
||||||
)
|
)
|
||||||
|
@ -3,7 +3,6 @@ package space.kscience.visionforge.html
|
|||||||
import kotlinx.html.FlowContent
|
import kotlinx.html.FlowContent
|
||||||
import kotlinx.html.TagConsumer
|
import kotlinx.html.TagConsumer
|
||||||
import kotlinx.html.stream.createHTML
|
import kotlinx.html.stream.createHTML
|
||||||
import space.kscience.dataforge.misc.DFExperimental
|
|
||||||
|
|
||||||
public typealias HtmlFragment = TagConsumer<*>.() -> Unit
|
public typealias HtmlFragment = TagConsumer<*>.() -> Unit
|
||||||
|
|
||||||
@ -16,8 +15,3 @@ public fun TagConsumer<*>.fragment(fragment: HtmlFragment) {
|
|||||||
public fun FlowContent.fragment(fragment: HtmlFragment) {
|
public fun FlowContent.fragment(fragment: HtmlFragment) {
|
||||||
fragment(consumer)
|
fragment(consumer)
|
||||||
}
|
}
|
||||||
|
|
||||||
public typealias HtmlVisionFragment = VisionTagConsumer<*>.() -> Unit
|
|
||||||
|
|
||||||
@DFExperimental
|
|
||||||
public fun HtmlVisionFragment(content: VisionTagConsumer<*>.() -> Unit): HtmlVisionFragment = content
|
|
@ -6,26 +6,47 @@ import space.kscience.dataforge.misc.DFExperimental
|
|||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.Name
|
||||||
import space.kscience.visionforge.Vision
|
import space.kscience.visionforge.Vision
|
||||||
import space.kscience.visionforge.VisionManager
|
import space.kscience.visionforge.VisionManager
|
||||||
|
import kotlin.random.Random
|
||||||
|
import kotlin.random.nextUInt
|
||||||
|
|
||||||
public fun TagConsumer<*>.embedVisionFragment(
|
public typealias HtmlVisionFragment = VisionTagConsumer<*>.() -> Unit
|
||||||
|
|
||||||
|
@DFExperimental
|
||||||
|
public fun HtmlVisionFragment(content: VisionTagConsumer<*>.() -> Unit): HtmlVisionFragment = content
|
||||||
|
|
||||||
|
|
||||||
|
internal const val RENDER_FUNCTION_NAME = "renderAllVisionsById"
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render a fragment in the given consumer and return a map of extracted visions
|
||||||
|
* @param manager a VisionManager used for serialization
|
||||||
|
* @param embedData embed Vision initial state in the HTML
|
||||||
|
* @param fetchDataUrl fetch data after first render from given url
|
||||||
|
* @param fetchUpdatesUrl receive push updates from the server at given url
|
||||||
|
* @param idPrefix a prefix to be used before vision ids
|
||||||
|
* @param renderScript if true add rendering script after the fragment
|
||||||
|
*/
|
||||||
|
public fun TagConsumer<*>.visionFragment(
|
||||||
manager: VisionManager,
|
manager: VisionManager,
|
||||||
embedData: Boolean = true,
|
embedData: Boolean = true,
|
||||||
fetchData: String? = null,
|
fetchDataUrl: String? = null,
|
||||||
fetchUpdates: String? = null,
|
fetchUpdatesUrl: String? = null,
|
||||||
idPrefix: String? = null,
|
idPrefix: String? = null,
|
||||||
|
renderScript: Boolean = true,
|
||||||
fragment: HtmlVisionFragment,
|
fragment: HtmlVisionFragment,
|
||||||
): Map<Name, Vision> {
|
): Map<Name, Vision> {
|
||||||
val visionMap = HashMap<Name, Vision>()
|
val visionMap = HashMap<Name, Vision>()
|
||||||
val consumer = object : VisionTagConsumer<Any?>(this@embedVisionFragment, manager, idPrefix) {
|
val consumer = object : VisionTagConsumer<Any?>(this@visionFragment, manager, idPrefix) {
|
||||||
override fun DIV.renderVision(name: Name, vision: Vision, outputMeta: Meta) {
|
override fun DIV.renderVision(name: Name, vision: Vision, outputMeta: Meta) {
|
||||||
visionMap[name] = vision
|
visionMap[name] = vision
|
||||||
// Toggle update mode
|
// Toggle update mode
|
||||||
|
|
||||||
fetchUpdates?.let {
|
fetchUpdatesUrl?.let {
|
||||||
attributes[OUTPUT_CONNECT_ATTRIBUTE] = it
|
attributes[OUTPUT_CONNECT_ATTRIBUTE] = it
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchData?.let {
|
fetchDataUrl?.let {
|
||||||
attributes[OUTPUT_FETCH_ATTRIBUTE] = it
|
attributes[OUTPUT_FETCH_ATTRIBUTE] = it
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,39 +61,36 @@ public fun TagConsumer<*>.embedVisionFragment(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (renderScript) {
|
||||||
|
val id = "fragment[${fragment.hashCode()}/${Random.nextUInt()}]"
|
||||||
|
div {
|
||||||
|
this.id = id
|
||||||
fragment(consumer)
|
fragment(consumer)
|
||||||
return visionMap
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun FlowContent.embedVisionFragment(
|
|
||||||
manager: VisionManager,
|
|
||||||
embedData: Boolean = true,
|
|
||||||
fetchDataUrl: String? = null,
|
|
||||||
fetchUpdatesUrl: String? = null,
|
|
||||||
idPrefix: String? = null,
|
|
||||||
fragment: HtmlVisionFragment,
|
|
||||||
): Map<Name, Vision> = consumer.embedVisionFragment(manager, embedData, fetchDataUrl, fetchUpdatesUrl, idPrefix, fragment)
|
|
||||||
|
|
||||||
|
|
||||||
internal const val RENDER_FUNCTION_NAME = "renderAllVisionsById"
|
|
||||||
|
|
||||||
public fun TagConsumer<*>.embedAndRenderVisionFragment(
|
|
||||||
manager: VisionManager,
|
|
||||||
id: Any,
|
|
||||||
embedData: Boolean = true,
|
|
||||||
fetchData: String? = null,
|
|
||||||
fetchUpdates: String? = null,
|
|
||||||
idPrefix: String? = null,
|
|
||||||
fragment: HtmlVisionFragment,
|
|
||||||
) {
|
|
||||||
div {
|
|
||||||
div {
|
|
||||||
this.id = id.toString()
|
|
||||||
embedVisionFragment(manager, embedData, fetchData, fetchUpdates, idPrefix, fragment)
|
|
||||||
}
|
}
|
||||||
script {
|
script {
|
||||||
type = "text/javascript"
|
type = "text/javascript"
|
||||||
unsafe { +"window.${RENDER_FUNCTION_NAME}(\"$id\");" }
|
unsafe { +"window.${RENDER_FUNCTION_NAME}(\"$id\");" }
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
fragment(consumer)
|
||||||
}
|
}
|
||||||
|
return visionMap
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public fun FlowContent.visionFragment(
|
||||||
|
manager: VisionManager,
|
||||||
|
embedData: Boolean = true,
|
||||||
|
fetchDataUrl: String? = null,
|
||||||
|
fetchUpdatesUrl: String? = null,
|
||||||
|
idPrefix: String? = null,
|
||||||
|
renderSctipt: Boolean = true,
|
||||||
|
fragment: HtmlVisionFragment,
|
||||||
|
): Map<Name, Vision> = consumer.visionFragment(
|
||||||
|
manager,
|
||||||
|
embedData,
|
||||||
|
fetchDataUrl,
|
||||||
|
fetchUpdatesUrl,
|
||||||
|
idPrefix,
|
||||||
|
renderSctipt,
|
||||||
|
fragment
|
||||||
|
)
|
@ -1,12 +1,11 @@
|
|||||||
package space.kscience.visionforge.html
|
package space.kscience.visionforge.html
|
||||||
|
|
||||||
import kotlinx.html.*
|
import kotlinx.html.*
|
||||||
import space.kscience.dataforge.context.Context
|
|
||||||
import space.kscience.dataforge.misc.DFExperimental
|
import space.kscience.dataforge.misc.DFExperimental
|
||||||
import space.kscience.visionforge.visionManager
|
import space.kscience.visionforge.VisionManager
|
||||||
|
|
||||||
public data class Page(
|
public data class Page(
|
||||||
public val context: Context,
|
public val visionManager: VisionManager,
|
||||||
public val title: String,
|
public val title: String,
|
||||||
public val headers: Map<String, HtmlFragment>,
|
public val headers: Map<String, HtmlFragment>,
|
||||||
public val content: HtmlVisionFragment,
|
public val content: HtmlVisionFragment,
|
||||||
@ -22,14 +21,14 @@ public data class Page(
|
|||||||
title(this@Page.title)
|
title(this@Page.title)
|
||||||
}
|
}
|
||||||
body {
|
body {
|
||||||
embedVisionFragment(context.visionManager, fragment = content)
|
visionFragment(visionManager, fragment = content)
|
||||||
}
|
}
|
||||||
}.finalize()
|
}.finalize()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@DFExperimental
|
@DFExperimental
|
||||||
public fun Context.page(
|
public fun VisionManager.page(
|
||||||
title: String = "VisionForge page",
|
title: String = "VisionForge page",
|
||||||
vararg headers: Pair<String, HtmlFragment>,
|
vararg headers: Pair<String, HtmlFragment>,
|
||||||
content: HtmlVisionFragment,
|
content: HtmlVisionFragment,
|
||||||
|
@ -7,6 +7,7 @@ import kotlinx.html.id
|
|||||||
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.get
|
||||||
import space.kscience.dataforge.meta.node
|
import space.kscience.dataforge.meta.node
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@ -17,10 +18,29 @@ public class VisionOfHtmlForm(
|
|||||||
public var values: Meta? by meta.node()
|
public var values: Meta? by meta.node()
|
||||||
}
|
}
|
||||||
|
|
||||||
public inline fun <R> TagConsumer<R>.visionOfForm(id: String, crossinline builder: FORM.() -> Unit): VisionOfHtmlForm {
|
public class HtmlFormFragment internal constructor(
|
||||||
|
public val vision: VisionOfHtmlForm,
|
||||||
|
public val formBody: HtmlFragment,
|
||||||
|
){
|
||||||
|
public val values: Meta? get() = vision.values
|
||||||
|
public operator fun get(valueName: String): Meta? = values?.get(valueName)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun HtmlFormFragment(id: String? = null, builder: FORM.() -> Unit): HtmlFormFragment {
|
||||||
|
val realId = id ?: "form[${builder.hashCode().toUInt()}]"
|
||||||
|
return HtmlFormFragment(VisionOfHtmlForm(realId)) {
|
||||||
form {
|
form {
|
||||||
this.id = id
|
this.id = realId
|
||||||
builder()
|
builder()
|
||||||
}
|
}
|
||||||
return VisionOfHtmlForm(id)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun <R> TagConsumer<R>.formFragment(
|
||||||
|
id: String? = null,
|
||||||
|
builder: FORM.() -> Unit,
|
||||||
|
): VisionOfHtmlForm {
|
||||||
|
val formFragment = HtmlFormFragment(id, builder)
|
||||||
|
fragment(formFragment.formBody)
|
||||||
|
return formFragment.vision
|
||||||
}
|
}
|
@ -42,7 +42,7 @@ public abstract class VisionTagConsumer<R>(
|
|||||||
private val idPrefix: String? = null,
|
private val idPrefix: String? = null,
|
||||||
) : TagConsumer<R> by root {
|
) : TagConsumer<R> by root {
|
||||||
|
|
||||||
public open fun resolveId(name: Name): String = (idPrefix ?: "output:") + name.toString()
|
public open fun resolveId(name: Name): String = (idPrefix ?: "output") + "[$name]"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render a vision inside the output fragment
|
* Render a vision inside the output fragment
|
||||||
|
@ -164,7 +164,7 @@ public class VisionClient : AbstractPlugin() {
|
|||||||
val endpoint = resolveEndpoint(element)
|
val endpoint = resolveEndpoint(element)
|
||||||
logger.info { "Vision server is resolved to $endpoint" }
|
logger.info { "Vision server is resolved to $endpoint" }
|
||||||
URL(endpoint).apply {
|
URL(endpoint).apply {
|
||||||
pathname += "/vision"
|
pathname += "/data"
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
URL(attr.value)
|
URL(attr.value)
|
||||||
|
@ -94,7 +94,7 @@ public val formVisionRenderer: ElementVisionRenderer = ElementVisionRenderer<Vis
|
|||||||
form.onsubmit = { event ->
|
form.onsubmit = { event ->
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
val formData = FormData(form).toMeta()
|
val formData = FormData(form).toMeta()
|
||||||
console.log(formData.toString())
|
//console.log(formData.toString())
|
||||||
vision.values = formData
|
vision.values = formData
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
@ -4,12 +4,10 @@ import io.ktor.application.*
|
|||||||
import io.ktor.features.CORS
|
import io.ktor.features.CORS
|
||||||
import io.ktor.features.CallLogging
|
import io.ktor.features.CallLogging
|
||||||
import io.ktor.html.respondHtml
|
import io.ktor.html.respondHtml
|
||||||
import io.ktor.http.ContentType
|
import io.ktor.http.*
|
||||||
import io.ktor.http.HttpStatusCode
|
|
||||||
import io.ktor.http.cio.websocket.Frame
|
import io.ktor.http.cio.websocket.Frame
|
||||||
import io.ktor.http.content.resources
|
import io.ktor.http.content.resources
|
||||||
import io.ktor.http.content.static
|
import io.ktor.http.content.static
|
||||||
import io.ktor.http.withCharset
|
|
||||||
import io.ktor.response.respond
|
import io.ktor.response.respond
|
||||||
import io.ktor.response.respondText
|
import io.ktor.response.respondText
|
||||||
import io.ktor.routing.*
|
import io.ktor.routing.*
|
||||||
@ -25,8 +23,6 @@ import kotlinx.coroutines.launch
|
|||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.html.*
|
import kotlinx.html.*
|
||||||
import kotlinx.html.stream.createHTML
|
import kotlinx.html.stream.createHTML
|
||||||
import space.kscience.dataforge.context.Context
|
|
||||||
import space.kscience.dataforge.context.fetch
|
|
||||||
import space.kscience.dataforge.meta.*
|
import space.kscience.dataforge.meta.*
|
||||||
import space.kscience.dataforge.misc.DFExperimental
|
import space.kscience.dataforge.misc.DFExperimental
|
||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.Name
|
||||||
@ -34,7 +30,10 @@ import space.kscience.visionforge.Vision
|
|||||||
import space.kscience.visionforge.VisionChange
|
import space.kscience.visionforge.VisionChange
|
||||||
import space.kscience.visionforge.VisionManager
|
import space.kscience.visionforge.VisionManager
|
||||||
import space.kscience.visionforge.flowChanges
|
import space.kscience.visionforge.flowChanges
|
||||||
import space.kscience.visionforge.html.*
|
import space.kscience.visionforge.html.HtmlFragment
|
||||||
|
import space.kscience.visionforge.html.HtmlVisionFragment
|
||||||
|
import space.kscience.visionforge.html.fragment
|
||||||
|
import space.kscience.visionforge.html.visionFragment
|
||||||
import space.kscience.visionforge.three.server.VisionServer.Companion.DEFAULT_PAGE
|
import space.kscience.visionforge.three.server.VisionServer.Companion.DEFAULT_PAGE
|
||||||
import java.awt.Desktop
|
import java.awt.Desktop
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
@ -43,12 +42,13 @@ import kotlin.time.Duration.Companion.milliseconds
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A ktor plugin container with given [routing]
|
* A ktor plugin container with given [routing]
|
||||||
|
* @param serverUrl a server url including root route
|
||||||
*/
|
*/
|
||||||
public class VisionServer internal constructor(
|
public class VisionServer internal constructor(
|
||||||
private val visionManager: VisionManager,
|
private val visionManager: VisionManager,
|
||||||
private val application: Application,
|
private val serverUrl: Url,
|
||||||
private val rootRoute: String,
|
private val root: Route,
|
||||||
) : Configurable, CoroutineScope by application {
|
) : Configurable, CoroutineScope by root.application {
|
||||||
override val meta: ObservableMutableMeta = MutableMeta()
|
override val meta: ObservableMutableMeta = MutableMeta()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -74,7 +74,7 @@ public class VisionServer internal constructor(
|
|||||||
/**
|
/**
|
||||||
* Connect to server to get pushes. The address of the server is embedded in the tag. Default: `true`
|
* Connect to server to get pushes. The address of the server is embedded in the tag. Default: `true`
|
||||||
*/
|
*/
|
||||||
public var dataConnect: Boolean by meta.boolean(true, Name.parse("data.connect"))
|
public var dataUpdate: Boolean by meta.boolean(true, Name.parse("data.update"))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* a list of headers that should be applied to all pages
|
* a list of headers that should be applied to all pages
|
||||||
@ -90,6 +90,7 @@ public class VisionServer internal constructor(
|
|||||||
|
|
||||||
private fun HTML.visionPage(
|
private fun HTML.visionPage(
|
||||||
title: String,
|
title: String,
|
||||||
|
pagePath: String,
|
||||||
headers: List<HtmlFragment>,
|
headers: List<HtmlFragment>,
|
||||||
visionFragment: HtmlVisionFragment,
|
visionFragment: HtmlVisionFragment,
|
||||||
): Map<Name, Vision> {
|
): Map<Name, Vision> {
|
||||||
@ -106,11 +107,10 @@ public class VisionServer internal constructor(
|
|||||||
}
|
}
|
||||||
body {
|
body {
|
||||||
//Load the fragment and remember all loaded visions
|
//Load the fragment and remember all loaded visions
|
||||||
visionMap = embedVisionFragment(
|
visionMap = visionFragment(
|
||||||
manager = visionManager,
|
manager = visionManager,
|
||||||
embedData = true,
|
embedData = true,
|
||||||
fetchDataUrl = VisionTagConsumer.AUTO_DATA_ATTRIBUTE,
|
fetchUpdatesUrl = "$serverUrl$pagePath/ws",
|
||||||
fetchUpdatesUrl = VisionTagConsumer.AUTO_DATA_ATTRIBUTE,
|
|
||||||
fragment = visionFragment
|
fragment = visionFragment
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -122,7 +122,7 @@ public class VisionServer internal constructor(
|
|||||||
* Server a map of visions without providing explicit html page for them
|
* Server a map of visions without providing explicit html page for them
|
||||||
*/
|
*/
|
||||||
@OptIn(DFExperimental::class)
|
@OptIn(DFExperimental::class)
|
||||||
internal fun serveVisions(route: Route, visions: Map<Name, Vision>): Unit = route {
|
private fun serveVisions(route: Route, visions: Map<Name, Vision>): Unit = route {
|
||||||
application.log.info("Serving visions $visions at $route")
|
application.log.info("Serving visions $visions at $route")
|
||||||
|
|
||||||
//Update websocket
|
//Update websocket
|
||||||
@ -157,7 +157,7 @@ public class VisionServer internal constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
//Plots in their json representation
|
//Plots in their json representation
|
||||||
get("vision") {
|
get("data") {
|
||||||
val name: String = call.request.queryParameters["name"]
|
val name: String = call.request.queryParameters["name"]
|
||||||
?: error("Vision name is not defined in parameters")
|
?: error("Vision name is not defined in parameters")
|
||||||
|
|
||||||
@ -174,47 +174,40 @@ public class VisionServer internal constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a static html page and serve visions produced in the process
|
|
||||||
*/
|
|
||||||
@DFExperimental
|
|
||||||
public fun createHtmlAndServe(
|
|
||||||
route: String,
|
|
||||||
title: String,
|
|
||||||
headers: List<HtmlFragment>,
|
|
||||||
visionFragment: HtmlVisionFragment,
|
|
||||||
): String {
|
|
||||||
val htmlString = createHTML().apply {
|
|
||||||
html {
|
|
||||||
visionPage(title, headers, visionFragment).also {
|
|
||||||
serveVisions(route, it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.finalize()
|
|
||||||
|
|
||||||
return htmlString
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serve visions in a given [route] without providing a page template
|
* Serve visions in a given [route] without providing a page template
|
||||||
*/
|
*/
|
||||||
public fun serveVisions(route: String, visions: Map<Name, Vision>): Unit {
|
public fun serveVisions(route: String, visions: Map<Name, Vision>): Unit {
|
||||||
application.routing {
|
root.route(route) {
|
||||||
route(rootRoute) {
|
|
||||||
route(route) {
|
|
||||||
serveVisions(this, visions)
|
serveVisions(this, visions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serve a page, potentially containing any number of visions at a given [route] with given [headers].
|
* Compile a fragment to string and serve visions from it
|
||||||
|
*/
|
||||||
|
public fun serveVisionsFromFragment(
|
||||||
|
route: String,
|
||||||
|
fragment: HtmlVisionFragment,
|
||||||
|
): String = createHTML().apply {
|
||||||
|
val visions = visionFragment(
|
||||||
|
visionManager,
|
||||||
|
embedData = true,
|
||||||
|
fetchUpdatesUrl = "$serverUrl$route/ws",
|
||||||
|
renderScript = true,
|
||||||
|
fragment = fragment
|
||||||
|
)
|
||||||
|
serveVisions(route, visions)
|
||||||
|
}.finalize()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serve a page, potentially containing any number of visions at a given [pagePath] with given [headers].
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public fun page(
|
public fun page(
|
||||||
route: String = DEFAULT_PAGE,
|
pagePath: String = DEFAULT_PAGE,
|
||||||
title: String = "VisionForge server page '$route'",
|
title: String = "VisionForge server page '$pagePath'",
|
||||||
headers: List<HtmlFragment> = emptyList(),
|
headers: List<HtmlFragment> = emptyList(),
|
||||||
visionFragment: HtmlVisionFragment,
|
visionFragment: HtmlVisionFragment,
|
||||||
) {
|
) {
|
||||||
@ -223,15 +216,13 @@ public class VisionServer internal constructor(
|
|||||||
val cachedHtml: String? = if (cacheFragments) {
|
val cachedHtml: String? = if (cacheFragments) {
|
||||||
//Create and cache page html and map of visions
|
//Create and cache page html and map of visions
|
||||||
createHTML(true).html {
|
createHTML(true).html {
|
||||||
visions.putAll(visionPage(title, headers, visionFragment))
|
visions.putAll(visionPage(title, pagePath, headers, visionFragment))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
application.routing {
|
root.route(pagePath) {
|
||||||
route(rootRoute) {
|
|
||||||
route(route) {
|
|
||||||
serveVisions(this, visions)
|
serveVisions(this, visions)
|
||||||
//filled pages
|
//filled pages
|
||||||
get {
|
get {
|
||||||
@ -239,7 +230,7 @@ public class VisionServer internal constructor(
|
|||||||
//re-create html and vision list on each call
|
//re-create html and vision list on each call
|
||||||
call.respondHtml {
|
call.respondHtml {
|
||||||
visions.clear()
|
visions.clear()
|
||||||
visions.putAll(visionPage(title, headers, visionFragment))
|
visions.putAll(visionPage(title, pagePath, headers, visionFragment))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
//Use cached html
|
//Use cached html
|
||||||
@ -247,11 +238,12 @@ public class VisionServer internal constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public companion object {
|
public companion object {
|
||||||
|
public const val DEFAULT_PORT: Int = 7777
|
||||||
public const val DEFAULT_PAGE: String = "/"
|
public const val DEFAULT_PAGE: String = "/"
|
||||||
public val UPDATE_INTERVAL_KEY: Name = Name.parse("update.interval")
|
public val UPDATE_INTERVAL_KEY: Name = Name.parse("update.interval")
|
||||||
}
|
}
|
||||||
@ -286,7 +278,11 @@ public inline fun VisionServer.useCss(href: String, crossinline block: LINK.() -
|
|||||||
/**
|
/**
|
||||||
* Attach VisionForge server application to given server
|
* Attach VisionForge server application to given server
|
||||||
*/
|
*/
|
||||||
public fun Application.visionServer(context: Context, route: String = DEFAULT_PAGE): VisionServer {
|
public fun Application.visionServer(
|
||||||
|
visionManager: VisionManager,
|
||||||
|
webServerUrl: Url,
|
||||||
|
path: String = DEFAULT_PAGE,
|
||||||
|
): VisionServer {
|
||||||
if (featureOrNull(WebSockets) == null) {
|
if (featureOrNull(WebSockets) == null) {
|
||||||
install(WebSockets)
|
install(WebSockets)
|
||||||
}
|
}
|
||||||
@ -301,17 +297,15 @@ public fun Application.visionServer(context: Context, route: String = DEFAULT_PA
|
|||||||
install(CallLogging)
|
install(CallLogging)
|
||||||
}
|
}
|
||||||
|
|
||||||
val visionManager = context.fetch(VisionManager)
|
val serverRoute = (featureOrNull(Routing) ?: install(Routing)).createRouteFromPath(path)
|
||||||
|
|
||||||
routing {
|
serverRoute {
|
||||||
route(route) {
|
|
||||||
static {
|
static {
|
||||||
resources()
|
resources()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return VisionServer(visionManager, this, route)
|
return VisionServer(visionManager, webServerUrl.copy(encodedPath = path), serverRoute)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -319,10 +313,11 @@ public fun Application.visionServer(context: Context, route: String = DEFAULT_PA
|
|||||||
*/
|
*/
|
||||||
public fun VisionManager.serve(
|
public fun VisionManager.serve(
|
||||||
host: String = "localhost",
|
host: String = "localhost",
|
||||||
port: Int = 7777,
|
port: Int = VisionServer.DEFAULT_PORT,
|
||||||
block: VisionServer.() -> Unit,
|
block: VisionServer.() -> Unit,
|
||||||
): ApplicationEngine = context.embeddedServer(CIO, port, host) {
|
): ApplicationEngine = context.embeddedServer(CIO, port, host) {
|
||||||
visionServer(context).apply(block)
|
val url = URLBuilder(host = host, port = port).build()
|
||||||
|
visionServer(this@serve, url).apply(block)
|
||||||
}.start()
|
}.start()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -47,7 +47,7 @@ public abstract class MeshThreeFactory<in T : Solid>(
|
|||||||
obj.onPropertyChange { name ->
|
obj.onPropertyChange { name ->
|
||||||
when {
|
when {
|
||||||
name.startsWith(Solid.GEOMETRY_KEY) -> {
|
name.startsWith(Solid.GEOMETRY_KEY) -> {
|
||||||
val oldGeometry = mesh.geometry as BufferGeometry
|
val oldGeometry = mesh.geometry
|
||||||
val newGeometry = buildGeometry(obj)
|
val newGeometry = buildGeometry(obj)
|
||||||
oldGeometry.attributes = newGeometry.attributes
|
oldGeometry.attributes = newGeometry.attributes
|
||||||
//mesh.applyWireFrame(obj)
|
//mesh.applyWireFrame(obj)
|
||||||
|
@ -53,7 +53,7 @@ public class ThreeCanvas(
|
|||||||
private val mousePosition: Vector2 = Vector2()
|
private val mousePosition: Vector2 = Vector2()
|
||||||
|
|
||||||
private val scene: Scene = Scene().apply {
|
private val scene: Scene = Scene().apply {
|
||||||
options.useProperty(Canvas3DOptions::axes, this) { axesConfig ->
|
options.useProperty(Canvas3DOptions::axes, this) {
|
||||||
getObjectByName(AXES_NAME)?.let { remove(it) }
|
getObjectByName(AXES_NAME)?.let { remove(it) }
|
||||||
val axesObject = AxesHelper(axes.size.toInt()).apply { visible = axes.visible }
|
val axesObject = AxesHelper(axes.size.toInt()).apply { visible = axes.visible }
|
||||||
axesObject.name = AXES_NAME
|
axesObject.name = AXES_NAME
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package space.kscience.visionforge.three.server
|
package space.kscience.visionforge.three.server
|
||||||
|
|
||||||
import space.kscience.dataforge.context.Context
|
|
||||||
import space.kscience.dataforge.misc.DFExperimental
|
import space.kscience.dataforge.misc.DFExperimental
|
||||||
|
import space.kscience.visionforge.VisionManager
|
||||||
import space.kscience.visionforge.html.HtmlVisionFragment
|
import space.kscience.visionforge.html.HtmlVisionFragment
|
||||||
import space.kscience.visionforge.html.ResourceLocation
|
import space.kscience.visionforge.html.ResourceLocation
|
||||||
import space.kscience.visionforge.html.page
|
import space.kscience.visionforge.html.page
|
||||||
@ -16,7 +16,7 @@ public fun VisionServer.useThreeJs(): Unit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@DFExperimental
|
@DFExperimental
|
||||||
public fun Context.makeThreeJsFile(
|
public fun VisionManager.makeThreeJsFile(
|
||||||
content: HtmlVisionFragment,
|
content: HtmlVisionFragment,
|
||||||
path: Path? = null,
|
path: Path? = null,
|
||||||
title: String = "VisionForge page",
|
title: String = "VisionForge page",
|
||||||
|
Loading…
Reference in New Issue
Block a user