First server iteration.
This commit is contained in:
parent
2be4576495
commit
ebb7bf72d1
@ -2,6 +2,7 @@ package hep.dataforge.vision.html
|
|||||||
|
|
||||||
import hep.dataforge.names.Name
|
import hep.dataforge.names.Name
|
||||||
import hep.dataforge.vision.Vision
|
import hep.dataforge.vision.Vision
|
||||||
|
import kotlinx.html.FlowContent
|
||||||
import kotlinx.html.TagConsumer
|
import kotlinx.html.TagConsumer
|
||||||
|
|
||||||
public class BindingHtmlOutputScope<T, V : Vision>(
|
public class BindingHtmlOutputScope<T, V : Vision>(
|
||||||
@ -16,3 +17,11 @@ public class BindingHtmlOutputScope<T, V : Vision>(
|
|||||||
_bindings[htmlOutput.name] = vision
|
_bindings[htmlOutput.name] = vision
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public fun <T : Any> TagConsumer<T>.visionFragment(fragment: HtmlVisionFragment<Vision>): Map<Name, Vision> {
|
||||||
|
return BindingHtmlOutputScope<T, Vision>(this).apply(fragment.content).bindings
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun FlowContent.visionFragment(fragment: HtmlVisionFragment<Vision>): Map<Name, Vision> {
|
||||||
|
return BindingHtmlOutputScope<Any?, Vision>(consumer).apply(fragment.content).bindings
|
||||||
|
}
|
@ -3,10 +3,7 @@ package hep.dataforge.vision.html
|
|||||||
import hep.dataforge.names.Name
|
import hep.dataforge.names.Name
|
||||||
import hep.dataforge.names.toName
|
import hep.dataforge.names.toName
|
||||||
import hep.dataforge.vision.Vision
|
import hep.dataforge.vision.Vision
|
||||||
import kotlinx.html.DIV
|
import kotlinx.html.*
|
||||||
import kotlinx.html.TagConsumer
|
|
||||||
import kotlinx.html.div
|
|
||||||
import kotlinx.html.id
|
|
||||||
|
|
||||||
public class HtmlOutput<V : Vision>(
|
public class HtmlOutput<V : Vision>(
|
||||||
public val outputScope: HtmlOutputScope<*, V>,
|
public val outputScope: HtmlOutputScope<*, V>,
|
||||||
@ -28,7 +25,9 @@ public abstract class HtmlOutputScope<R, V : Vision>(
|
|||||||
name: Name,
|
name: Name,
|
||||||
crossinline block: HtmlOutput<V>.() -> Unit = {},
|
crossinline block: HtmlOutput<V>.() -> Unit = {},
|
||||||
): T = div {
|
): T = div {
|
||||||
this.id = resolveId(name)
|
id = resolveId(name)
|
||||||
|
classes = setOf(OUTPUT_CLASS)
|
||||||
|
attributes[NAME_ATTRIBUTE] = name.toString()
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
HtmlOutput(this@HtmlOutputScope, name, this).block()
|
HtmlOutput(this@HtmlOutputScope, name, this).block()
|
||||||
}
|
}
|
||||||
@ -49,4 +48,20 @@ public abstract class HtmlOutputScope<R, V : Vision>(
|
|||||||
renderVision(this, vision)
|
renderVision(this, vision)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process the resulting object produced by [TagConsumer]
|
||||||
|
*/
|
||||||
|
protected open fun processResult(result: R) {
|
||||||
|
//do nothing by default
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun finalize(): R {
|
||||||
|
return root.finalize().also { processResult(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
public companion object {
|
||||||
|
public const val OUTPUT_CLASS: String = "visionforge-output"
|
||||||
|
public const val NAME_ATTRIBUTE: String = "data-output-name"
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,8 +1,20 @@
|
|||||||
package hep.dataforge.vision.html
|
package hep.dataforge.vision.html
|
||||||
|
|
||||||
import hep.dataforge.vision.Vision
|
import hep.dataforge.vision.Vision
|
||||||
|
import kotlinx.html.FlowContent
|
||||||
|
import kotlinx.html.TagConsumer
|
||||||
|
|
||||||
public class HtmlVisionFragment<V : Vision>(public val layout: HtmlOutputScope<out Any, V>.() -> Unit)
|
public class HtmlFragment(public val content: TagConsumer<*>.() -> Unit)
|
||||||
|
|
||||||
public fun buildVisionFragment(visit: HtmlOutputScope<out Any, Vision>.() -> Unit): HtmlVisionFragment<Vision> =
|
public fun TagConsumer<*>.fragment(fragment: HtmlFragment) {
|
||||||
HtmlVisionFragment(visit)
|
fragment.content(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun FlowContent.fragment(fragment: HtmlFragment) {
|
||||||
|
fragment.content(consumer)
|
||||||
|
}
|
||||||
|
|
||||||
|
public class HtmlVisionFragment<V : Vision>(public val content: HtmlOutputScope<*, V>.() -> Unit)
|
||||||
|
|
||||||
|
public fun buildVisionFragment(block: HtmlOutputScope<*, Vision>.() -> Unit): HtmlVisionFragment<Vision> =
|
||||||
|
HtmlVisionFragment(block)
|
||||||
|
@ -22,7 +22,7 @@ public fun <T : Any> HtmlVisionFragment<Vision>.renderToObject(
|
|||||||
root: TagConsumer<T>,
|
root: TagConsumer<T>,
|
||||||
prefix: String? = null,
|
prefix: String? = null,
|
||||||
renderer: HtmlVisionRenderer<Vision>,
|
renderer: HtmlVisionRenderer<Vision>,
|
||||||
): T = StaticHtmlOutputScope(root, prefix, renderer).apply(layout).finalize()
|
): T = StaticHtmlOutputScope(root, prefix, renderer).apply(content).finalize()
|
||||||
|
|
||||||
public fun HtmlVisionFragment<Vision>.renderToString(renderer: HtmlVisionRenderer<Vision>): String =
|
public fun HtmlVisionFragment<Vision>.renderToString(renderer: HtmlVisionRenderer<Vision>): String =
|
||||||
renderToObject(createHTML(), null, renderer)
|
renderToObject(createHTML(), null, renderer)
|
@ -1,31 +0,0 @@
|
|||||||
package hep.dataforge.vision.html
|
|
||||||
|
|
||||||
import hep.dataforge.vision.Vision
|
|
||||||
import kotlinx.browser.document
|
|
||||||
import kotlinx.html.TagConsumer
|
|
||||||
import org.w3c.dom.Element
|
|
||||||
import org.w3c.dom.HTMLElement
|
|
||||||
|
|
||||||
public interface HtmlVisionBinding<in V: Vision>{
|
|
||||||
public fun bind(element: Element, vision: V): Unit
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun <V: Vision> Map<String, V>.bind(binder: HtmlVisionBinding<V>){
|
|
||||||
forEach { (id, vision) ->
|
|
||||||
val element = document.getElementById(id) ?: error("Could not find element with id $id")
|
|
||||||
binder.bind(element, vision)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun HtmlVisionFragment<Vision>.bindToDocument(
|
|
||||||
root: TagConsumer<HTMLElement>,
|
|
||||||
binder: HtmlVisionBinding<Vision>,
|
|
||||||
): HTMLElement = BindingHtmlOutputScope<HTMLElement, Vision>(root).apply(layout).let { scope ->
|
|
||||||
scope.finalize().apply {
|
|
||||||
scope.bindings.forEach { (name, vision) ->
|
|
||||||
val id = scope.resolveId(name)
|
|
||||||
val element = document.getElementById(id) ?: error("Could not find element with name $name and id $id")
|
|
||||||
binder.bind(element, vision)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,53 @@
|
|||||||
|
package hep.dataforge.vision.html
|
||||||
|
|
||||||
|
import hep.dataforge.names.Name
|
||||||
|
import hep.dataforge.names.toName
|
||||||
|
import hep.dataforge.vision.Vision
|
||||||
|
import kotlinx.browser.document
|
||||||
|
import kotlinx.html.TagConsumer
|
||||||
|
import org.w3c.dom.*
|
||||||
|
|
||||||
|
public interface ElementVisionRenderer<in V : Vision> {
|
||||||
|
public fun render(element: Element, vision: V): Unit
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun <V : Vision> Map<String, V>.bind(renderer: ElementVisionRenderer<V>) {
|
||||||
|
forEach { (id, vision) ->
|
||||||
|
val element = document.getElementById(id) ?: error("Could not find element with id $id")
|
||||||
|
renderer.render(element, vision)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun <V : Vision> Element.renderVisions(renderer: ElementVisionRenderer<V>, visionProvider: (Name) -> V?) {
|
||||||
|
val elements = getElementsByClassName(HtmlOutputScope.OUTPUT_CLASS)
|
||||||
|
elements.asList().forEach { element ->
|
||||||
|
val name = element.attributes[HtmlOutputScope.NAME_ATTRIBUTE]?.value
|
||||||
|
if (name == null) {
|
||||||
|
console.error("Attribute ${HtmlOutputScope.NAME_ATTRIBUTE} not defined in the output element")
|
||||||
|
return@forEach
|
||||||
|
}
|
||||||
|
val vision = visionProvider(name.toName())
|
||||||
|
if (vision == null) {
|
||||||
|
console.error("Vision with name $name is not resolved")
|
||||||
|
return@forEach
|
||||||
|
}
|
||||||
|
renderer.render(element, vision)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun <V : Vision> Document.renderVisions(renderer: ElementVisionRenderer<V>, visionProvider: (Name) -> V?): Unit {
|
||||||
|
documentElement?.renderVisions(renderer, visionProvider)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun HtmlVisionFragment<Vision>.renderInDocument(
|
||||||
|
root: TagConsumer<HTMLElement>,
|
||||||
|
renderer: ElementVisionRenderer<Vision>,
|
||||||
|
): HTMLElement = BindingHtmlOutputScope<HTMLElement, Vision>(root).apply(content).let { scope ->
|
||||||
|
scope.finalize().apply {
|
||||||
|
scope.bindings.forEach { (name, vision) ->
|
||||||
|
val id = scope.resolveId(name)
|
||||||
|
val element = document.getElementById(id) ?: error("Could not find element with name $name and id $id")
|
||||||
|
renderer.render(element, vision)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,92 +1,137 @@
|
|||||||
//package hep.dataforge.vision.server
|
package hep.dataforge.vision.server
|
||||||
//
|
|
||||||
//import hep.dataforge.meta.*
|
import hep.dataforge.context.Context
|
||||||
//import hep.dataforge.names.Name
|
import hep.dataforge.meta.*
|
||||||
//import hep.dataforge.names.toName
|
import hep.dataforge.names.Name
|
||||||
//import hep.dataforge.vision.server.VisionServer.Companion.DEFAULT_PAGE
|
import hep.dataforge.names.toName
|
||||||
//import io.ktor.application.Application
|
import hep.dataforge.vision.Vision
|
||||||
//import io.ktor.application.featureOrNull
|
import hep.dataforge.vision.VisionManager
|
||||||
//import io.ktor.application.install
|
import hep.dataforge.vision.flowChanges
|
||||||
//import io.ktor.features.CORS
|
import hep.dataforge.vision.html.*
|
||||||
//import io.ktor.http.content.resources
|
import hep.dataforge.vision.server.VisionServer.Companion.DEFAULT_PAGE
|
||||||
//import io.ktor.http.content.static
|
import io.ktor.application.*
|
||||||
//import io.ktor.routing.Routing
|
import io.ktor.features.CORS
|
||||||
//import io.ktor.routing.route
|
import io.ktor.html.respondHtml
|
||||||
//import io.ktor.routing.routing
|
import io.ktor.http.*
|
||||||
//import io.ktor.server.engine.ApplicationEngine
|
import io.ktor.http.cio.websocket.Frame
|
||||||
//import io.ktor.websocket.WebSockets
|
import io.ktor.http.content.resources
|
||||||
//import kotlinx.html.TagConsumer
|
import io.ktor.http.content.static
|
||||||
//import java.awt.Desktop
|
import io.ktor.response.respond
|
||||||
//import java.net.URI
|
import io.ktor.response.respondText
|
||||||
//import kotlin.text.get
|
import io.ktor.routing.*
|
||||||
//
|
import io.ktor.server.engine.ApplicationEngine
|
||||||
//public enum class ServerUpdateMode {
|
import io.ktor.websocket.WebSockets
|
||||||
// NONE,
|
import io.ktor.websocket.webSocket
|
||||||
// PUSH,
|
import kotlinx.html.*
|
||||||
// PULL
|
import kotlinx.html.stream.createHTML
|
||||||
//}
|
import java.awt.Desktop
|
||||||
//
|
import java.net.URI
|
||||||
//public class VisionServer internal constructor(
|
import kotlin.time.milliseconds
|
||||||
// private val routing: Routing,
|
|
||||||
// private val rootRoute: String,
|
public class VisionServer internal constructor(
|
||||||
//) : Configurable {
|
private val visionManager: VisionManager,
|
||||||
// override val config: Config = Config()
|
private val routing: Routing,
|
||||||
// public var updateMode: ServerUpdateMode by config.enum(ServerUpdateMode.NONE, key = UPDATE_MODE_KEY)
|
private val rootRoute: String,
|
||||||
// public var updateInterval: Long by config.long(300, key = UPDATE_INTERVAL_KEY)
|
) : Configurable {
|
||||||
// public var embedData: Boolean by config.boolean(false)
|
override val config: Config = Config()
|
||||||
//
|
public var updateInterval: Long by config.long(300, key = UPDATE_INTERVAL_KEY)
|
||||||
// /**
|
public var cacheFragments: Boolean by config.boolean(true)
|
||||||
// * a list of headers that should be applied to all pages
|
|
||||||
// */
|
/**
|
||||||
// private val globalHeaders: ArrayList<HtmlFragment> = ArrayList<HtmlFragment>()
|
* a list of headers that should be applied to all pages
|
||||||
//
|
*/
|
||||||
// public fun header(block: TagConsumer<*>.() -> Unit) {
|
private val globalHeaders: ArrayList<HtmlFragment> = ArrayList()
|
||||||
// globalHeaders.add(HtmlFragment(block))
|
|
||||||
// }
|
public fun header(block: TagConsumer<*>.() -> Unit) {
|
||||||
//
|
globalHeaders.add(HtmlFragment(block))
|
||||||
// public fun page(
|
}
|
||||||
// plotlyFragment: PlotlyFragment,
|
|
||||||
// route: String = DEFAULT_PAGE,
|
private fun HTML.buildPage(
|
||||||
// title: String = "Plotly server page '$route'",
|
visionFragment: HtmlVisionFragment<Vision>,
|
||||||
// headers: List<HtmlFragment> = emptyList(),
|
title: String,
|
||||||
// ) {
|
headers: List<HtmlFragment>,
|
||||||
// routing.createRouteFromPath(rootRoute).apply {
|
): Map<Name, Vision> {
|
||||||
// val plots = HashMap<String, Plot>()
|
lateinit var result: Map<Name, Vision>
|
||||||
// route(route) {
|
|
||||||
// //Update websocket
|
head {
|
||||||
// webSocket("ws/{id}") {
|
meta {
|
||||||
// val plotId: String = call.parameters["id"] ?: error("Plot id not defined")
|
charset = "utf-8"
|
||||||
//
|
(globalHeaders + headers).forEach {
|
||||||
// application.log.debug("Opened server socket for $plotId")
|
fragment(it)
|
||||||
//
|
}
|
||||||
// val plot = plots[plotId] ?: error("Plot with id='$plotId' not registered")
|
}
|
||||||
//
|
title(title)
|
||||||
// try {
|
}
|
||||||
// plot.collectUpdates(plotId, this, updateInterval).collect { update ->
|
body {
|
||||||
// val json = update.toJson()
|
result = visionFragment(visionFragment)
|
||||||
// outgoing.send(Frame.Text(json.toString()))
|
script {
|
||||||
// }
|
type = "text/javascript"
|
||||||
// } catch (ex: Exception) {
|
|
||||||
// application.log.debug("Closed server socket for $plotId")
|
val normalizedRoute = if (rootRoute.endsWith("/")) {
|
||||||
// }
|
rootRoute
|
||||||
// }
|
} else {
|
||||||
// //Plots in their json representation
|
"$rootRoute/"
|
||||||
// get("data/{id}") {
|
}
|
||||||
// val id: String = call.parameters["id"] ?: error("Plot id not defined")
|
|
||||||
//
|
src = TODO()//"${normalizedRoute}js/plotlyConnect.js"
|
||||||
// val plot: Plot? = plots[id]
|
}
|
||||||
// if (plot == null) {
|
}
|
||||||
// call.respond(HttpStatusCode.NotFound, "Plot with id = $id not found")
|
|
||||||
// } else {
|
return result
|
||||||
// call.respondText(
|
}
|
||||||
// plot.toJsonString(),
|
|
||||||
// contentType = ContentType.Application.Json,
|
public fun page(
|
||||||
// status = HttpStatusCode.OK
|
visionFragment: HtmlVisionFragment<Vision>,
|
||||||
// )
|
route: String = DEFAULT_PAGE,
|
||||||
// }
|
title: String = "VisionForge server page '$route'",
|
||||||
// }
|
headers: List<HtmlFragment> = emptyList(),
|
||||||
// //filled pages
|
) {
|
||||||
// get {
|
val visions = HashMap<Name, Vision>()
|
||||||
|
|
||||||
|
val cachedHtml: String? = if (cacheFragments) {
|
||||||
|
createHTML(true).html {
|
||||||
|
visions.putAll(buildPage(visionFragment, title, headers))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
routing.createRouteFromPath(rootRoute).apply {
|
||||||
|
route(route) {
|
||||||
|
//Update websocket
|
||||||
|
webSocket("ws") {
|
||||||
|
val name: String = call.request.queryParameters["name"]
|
||||||
|
?: error("Vision name is not defined in parameters")
|
||||||
|
|
||||||
|
application.log.debug("Opened server socket for $name")
|
||||||
|
val vision: Vision = visions[name.toName()] ?: error("Plot with id='$name' not registered")
|
||||||
|
try {
|
||||||
|
vision.flowChanges(this, updateInterval.milliseconds).collect { update ->
|
||||||
|
val json = visionManager.encodeToString(update)
|
||||||
|
outgoing.send(Frame.Text(json))
|
||||||
|
}
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
application.log.debug("Closed server socket for $name")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//Plots in their json representation
|
||||||
|
get("vision") {
|
||||||
|
val name: String = call.request.queryParameters["name"]
|
||||||
|
?: error("Vision name is not defined in parameters")
|
||||||
|
|
||||||
|
val vision: Vision? = visions[name.toName()]
|
||||||
|
if (vision == null) {
|
||||||
|
call.respond(HttpStatusCode.NotFound, "Vision with name '$name' not found")
|
||||||
|
} else {
|
||||||
|
call.respondText(
|
||||||
|
visionManager.encodeToString(vision),
|
||||||
|
contentType = ContentType.Application.Json,
|
||||||
|
status = HttpStatusCode.OK
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//filled pages
|
||||||
|
get {
|
||||||
// val origin = call.request.origin
|
// val origin = call.request.origin
|
||||||
// val url = URLBuilder().apply {
|
// val url = URLBuilder().apply {
|
||||||
// protocol = URLProtocol.createOrDefault(origin.scheme)
|
// protocol = URLProtocol.createOrDefault(origin.scheme)
|
||||||
@ -95,125 +140,68 @@
|
|||||||
// port = origin.port
|
// port = origin.port
|
||||||
// encodedPath = origin.uri
|
// encodedPath = origin.uri
|
||||||
// }.build()
|
// }.build()
|
||||||
// call.respondHtml {
|
if (cachedHtml == null) {
|
||||||
// val normalizedRoute = if (rootRoute.endsWith("/")) {
|
call.respondHtml {
|
||||||
// rootRoute
|
visions.putAll(buildPage(visionFragment, title, headers))
|
||||||
// } else {
|
}
|
||||||
// "$rootRoute/"
|
} else {
|
||||||
// }
|
call.respondText(cachedHtml, ContentType.Text.Html.withCharset(Charsets.UTF_8))
|
||||||
//
|
}
|
||||||
// head {
|
}
|
||||||
// meta {
|
}
|
||||||
// charset = "utf-8"
|
}
|
||||||
// (globalHeaders + headers).forEach {
|
}
|
||||||
// it.visit(consumer)
|
|
||||||
// }
|
public fun page(
|
||||||
// script {
|
route: String = DEFAULT_PAGE,
|
||||||
// type = "text/javascript"
|
title: String = "Plotly server page '$route'",
|
||||||
// src = "${normalizedRoute}js/plotly.min.js"
|
headers: List<HtmlFragment> = emptyList(),
|
||||||
// }
|
content: HtmlOutputScope<*, Vision>.() -> Unit,
|
||||||
// script {
|
) {
|
||||||
// type = "text/javascript"
|
page(buildVisionFragment(content), route, title, headers)
|
||||||
// src = "${normalizedRoute}js/plotlyConnect.js"
|
}
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// title(title)
|
public companion object {
|
||||||
// }
|
public const val DEFAULT_PAGE: String = "/"
|
||||||
// body {
|
public val UPDATE_MODE_KEY: Name = "update.mode".toName()
|
||||||
// val container =
|
public val UPDATE_INTERVAL_KEY: Name = "update.interval".toName()
|
||||||
// ServerPlotlyRenderer(url, updateMode, updateInterval, embedData) { plotId, plot ->
|
}
|
||||||
// plots[plotId] = plot
|
}
|
||||||
// }
|
|
||||||
// with(plotlyFragment) {
|
|
||||||
// render(container)
|
/**
|
||||||
// }
|
* Attach plotly application to given server
|
||||||
// }
|
*/
|
||||||
// }
|
public fun Application.visionModule(context: Context, route: String = DEFAULT_PAGE): VisionServer {
|
||||||
// }
|
if (featureOrNull(WebSockets) == null) {
|
||||||
// }
|
install(WebSockets)
|
||||||
// }
|
}
|
||||||
// }
|
|
||||||
//
|
if (featureOrNull(CORS) == null) {
|
||||||
// public fun page(
|
install(CORS) {
|
||||||
// route: String = DEFAULT_PAGE,
|
anyHost()
|
||||||
// title: String = "Plotly server page '$route'",
|
}
|
||||||
// headers: List<HtmlFragment> = emptyList(),
|
}
|
||||||
// content: FlowContent.(renderer: PlotlyRenderer) -> Unit,
|
|
||||||
// ) {
|
|
||||||
// page(PlotlyFragment(content), route, title, headers)
|
val routing = routing {
|
||||||
// }
|
route(route) {
|
||||||
//
|
static {
|
||||||
//
|
resources()
|
||||||
// public companion object {
|
}
|
||||||
// public const val DEFAULT_PAGE: String = "/"
|
}
|
||||||
// public val UPDATE_MODE_KEY: Name = "update.mode".toName()
|
}
|
||||||
// public val UPDATE_INTERVAL_KEY: Name = "update.interval".toName()
|
|
||||||
// }
|
val visionManager = context.plugins.fetch(VisionManager)
|
||||||
//}
|
|
||||||
//
|
return VisionServer(visionManager, routing, route)
|
||||||
//
|
}
|
||||||
///**
|
|
||||||
// * Attach plotly application to given server
|
public fun ApplicationEngine.show() {
|
||||||
// */
|
val connector = environment.connectors.first()
|
||||||
//public fun Application.visionModule(route: String = DEFAULT_PAGE): VisionServer {
|
val uri = URI("http", null, connector.host, connector.port, null, null, null)
|
||||||
// if (featureOrNull(WebSockets) == null) {
|
Desktop.getDesktop().browse(uri)
|
||||||
// install(WebSockets)
|
}
|
||||||
// }
|
|
||||||
//
|
public fun ApplicationEngine.close(): Unit = stop(1000, 5000)
|
||||||
// if (featureOrNull(CORS) == null) {
|
|
||||||
// install(CORS) {
|
|
||||||
// anyHost()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// val routing = routing {
|
|
||||||
// route(route) {
|
|
||||||
// static {
|
|
||||||
// resources()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return VisionServer(routing, route)
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//
|
|
||||||
///**
|
|
||||||
// * Configure server to start sending updates in push mode. Does not affect loaded pages
|
|
||||||
// */
|
|
||||||
//public fun VisionServer.pushUpdates(interval: Long = 100): VisionServer = apply {
|
|
||||||
// updateMode = ServerUpdateMode.PUSH
|
|
||||||
// updateInterval = interval
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
///**
|
|
||||||
// * Configure client to request regular updates from server. Pull updates are more expensive than push updates since
|
|
||||||
// * they contain the full plot data and server can't decide what to send.
|
|
||||||
// */
|
|
||||||
//public fun VisionServer.pullUpdates(interval: Long = 1000): VisionServer = apply {
|
|
||||||
// updateMode = ServerUpdateMode.PULL
|
|
||||||
// updateInterval = interval
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
/////**
|
|
||||||
//// * Start static server (updates via reload)
|
|
||||||
//// */
|
|
||||||
////@OptIn(KtorExperimentalAPI::class)
|
|
||||||
////public fun Plotly.serve(
|
|
||||||
//// scope: CoroutineScope = GlobalScope,
|
|
||||||
//// host: String = "localhost",
|
|
||||||
//// port: Int = 7777,
|
|
||||||
//// block: PlotlyServer.() -> Unit,
|
|
||||||
////): ApplicationEngine = scope.embeddedServer(io.ktor.server.cio.CIO, port, host) {
|
|
||||||
//// plotlyModule().apply(block)
|
|
||||||
////}.start()
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//public fun ApplicationEngine.show() {
|
|
||||||
// val connector = environment.connectors.first()
|
|
||||||
// val uri = URI("http", null, connector.host, connector.port, null, null, null)
|
|
||||||
// Desktop.getDesktop().browse(uri)
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//public fun ApplicationEngine.close(): Unit = stop(1000, 5000)
|
|
@ -14,11 +14,5 @@ kotlin {
|
|||||||
api(project(":visionforge-core"))
|
api(project(":visionforge-core"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
jsMain {
|
|
||||||
dependencies {
|
|
||||||
implementation(npm("three", "0.122.0"))
|
|
||||||
implementation(npm("three-csg-ts", "1.0.1"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,7 +4,7 @@ import hep.dataforge.context.*
|
|||||||
import hep.dataforge.meta.Meta
|
import hep.dataforge.meta.Meta
|
||||||
import hep.dataforge.names.*
|
import hep.dataforge.names.*
|
||||||
import hep.dataforge.vision.Vision
|
import hep.dataforge.vision.Vision
|
||||||
import hep.dataforge.vision.html.HtmlVisionBinding
|
import hep.dataforge.vision.html.ElementVisionRenderer
|
||||||
import hep.dataforge.vision.solid.*
|
import hep.dataforge.vision.solid.*
|
||||||
import hep.dataforge.vision.solid.specifications.Canvas3DOptions
|
import hep.dataforge.vision.solid.specifications.Canvas3DOptions
|
||||||
import hep.dataforge.vision.visible
|
import hep.dataforge.vision.visible
|
||||||
@ -15,7 +15,7 @@ import kotlin.collections.set
|
|||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
import info.laht.threekt.objects.Group as ThreeGroup
|
import info.laht.threekt.objects.Group as ThreeGroup
|
||||||
|
|
||||||
public class ThreePlugin : AbstractPlugin(), HtmlVisionBinding<Solid> {
|
public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer<Solid> {
|
||||||
override val tag: PluginTag get() = Companion.tag
|
override val tag: PluginTag get() = Companion.tag
|
||||||
|
|
||||||
public val solidManager: SolidManager by require(SolidManager)
|
public val solidManager: SolidManager by require(SolidManager)
|
||||||
@ -122,7 +122,7 @@ public class ThreePlugin : AbstractPlugin(), HtmlVisionBinding<Solid> {
|
|||||||
attach(element)
|
attach(element)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun bind(element: Element, vision: Solid) {
|
override fun render(element: Element, vision: Solid) {
|
||||||
createCanvas(element).render(vision)
|
createCanvas(element).render(vision)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user