Add solids configuration to vision builder

This commit is contained in:
Alexander Nozik 2023-05-30 17:06:27 +03:00
parent f6f74b54f6
commit 18c39fc076
11 changed files with 76 additions and 71 deletions

View File

@ -13,7 +13,7 @@ val fxVersion by extra("11")
allprojects { allprojects {
group = "space.kscience" group = "space.kscience"
version = "0.3.0-dev-8" version = "0.3.0-dev-9"
} }
subprojects { subprojects {

11
demo/build.gradle.kts Normal file
View File

@ -0,0 +1,11 @@
import org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode
import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinPluginWrapper
subprojects {
plugins.withType<KotlinPluginWrapper> {
configure<KotlinProjectExtension> {
explicitApi = ExplicitApiMode.Disabled
}
}
}

View File

@ -30,6 +30,10 @@ kscience {
} }
} }
kotlin {
explicitApi = null
}
//kotlin { //kotlin {
// //
// sourceSets { // sourceSets {

View File

@ -2,7 +2,7 @@ package space.kscience.visionforge.examples
import io.ktor.server.cio.CIO import io.ktor.server.cio.CIO
import io.ktor.server.engine.embeddedServer import io.ktor.server.engine.embeddedServer
import io.ktor.server.http.content.resources import io.ktor.server.http.content.staticResources
import io.ktor.server.routing.routing import io.ktor.server.routing.routing
import kotlinx.html.* import kotlinx.html.*
import space.kscience.dataforge.context.Global import space.kscience.dataforge.context.Global
@ -12,21 +12,18 @@ import space.kscience.visionforge.html.VisionOfHtmlForm
import space.kscience.visionforge.html.VisionPage import space.kscience.visionforge.html.VisionPage
import space.kscience.visionforge.html.bindForm import space.kscience.visionforge.html.bindForm
import space.kscience.visionforge.onPropertyChange import space.kscience.visionforge.onPropertyChange
import space.kscience.visionforge.server.EngineConnectorConfig
import space.kscience.visionforge.server.close import space.kscience.visionforge.server.close
import space.kscience.visionforge.server.openInBrowser import space.kscience.visionforge.server.openInBrowser
import space.kscience.visionforge.server.visionPage import space.kscience.visionforge.server.visionPage
@Suppress("ExtractKtorModule")
fun main() { fun main() {
val visionManager = Global.request(VisionManager) val visionManager = Global.request(VisionManager)
val server = embeddedServer(CIO) {
val connector = EngineConnectorConfig("localhost", 7777)
val server = embeddedServer(CIO, connector.port, connector.host) {
routing { routing {
resources() staticResources("/", null)
} }
val form = VisionOfHtmlForm("form").apply { val form = VisionOfHtmlForm("form").apply {
@ -36,7 +33,6 @@ fun main() {
} }
visionPage( visionPage(
connector,
visionManager, visionManager,
VisionPage.scriptHeader("js/visionforge-playground.js"), VisionPage.scriptHeader("js/visionforge-playground.js"),
) { ) {

View File

@ -33,6 +33,7 @@ fun main() = makeVisionFile {
cylinder(30,20, name = "cylinder"){ cylinder(30,20, name = "cylinder"){
detail = 31 detail = 31
y = -220 y = -220
z = 15
} }
} }
} }

View File

@ -14,7 +14,6 @@ import space.kscience.dataforge.meta.Null
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.visionforge.Colors import space.kscience.visionforge.Colors
import space.kscience.visionforge.html.VisionPage import space.kscience.visionforge.html.VisionPage
import space.kscience.visionforge.server.EngineConnectorConfig
import space.kscience.visionforge.server.close import space.kscience.visionforge.server.close
import space.kscience.visionforge.server.openInBrowser import space.kscience.visionforge.server.openInBrowser
import space.kscience.visionforge.server.visionPage import space.kscience.visionforge.server.visionPage
@ -23,6 +22,7 @@ import space.kscience.visionforge.three.threeJsHeader
import kotlin.random.Random import kotlin.random.Random
@Suppress("ExtractKtorModule")
fun main() { fun main() {
val satContext = Context("sat") { val satContext = Context("sat") {
plugin(Solids) plugin(Solids)
@ -36,15 +36,12 @@ fun main() {
color.set(Colors.white) color.set(Colors.white)
} }
} }
val connector = EngineConnectorConfig("localhost", 7777) val server = embeddedServer(CIO, port = 7777) {
val server = embeddedServer(CIO, connector.port, connector.host) {
routing { routing {
staticResources("", null, null) staticResources("", null, null)
} }
visionPage( visionPage(
connector,
solids.visionManager, VisionPage.threeJsHeader, solids.visionManager, VisionPage.threeJsHeader,
VisionPage.styleSheetHeader("css/styles.css") VisionPage.styleSheetHeader("css/styles.css")
) { ) {

View File

@ -1,6 +1,5 @@
plugins { plugins {
id("space.kscience.gradle.mpp") id("space.kscience.gradle.mpp")
`maven-publish`
application application
} }
@ -22,8 +21,4 @@ kscience {
application { application {
mainClass.set("space.kscience.visionforge.solid.demo.FXDemoAppKt") mainClass.set("space.kscience.visionforge.solid.demo.FXDemoAppKt")
}
kotlin{
explicitApi = null
} }

View File

@ -20,7 +20,6 @@ import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.html.* import kotlinx.html.*
import kotlinx.html.stream.createHTML
import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Context
import space.kscience.dataforge.context.ContextAware import space.kscience.dataforge.context.ContextAware
import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.*
@ -96,7 +95,7 @@ public fun Application.serveVisionData(
val vision: Vision = resolveVision(Name.parse(name)) ?: error("Vision with id='$name' not registered") val vision: Vision = resolveVision(Name.parse(name)) ?: error("Vision with id='$name' not registered")
launch { launch {
for(frame in incoming) { for (frame in incoming) {
val data = frame.data.decodeToString() val data = frame.data.decodeToString()
application.log.debug("Received update for $name: \n$data") application.log.debug("Received update for $name: \n$data")
val change = configuration.visionManager.jsonFormat.decodeFromString( val change = configuration.visionManager.jsonFormat.decodeFromString(
@ -151,82 +150,82 @@ public fun Application.serveVisionData(
public fun Application.visionPage( public fun Application.visionPage(
route: String, route: String,
configuration: VisionRoute, configuration: VisionRoute,
connector: EngineConnectorConfig,
headers: Collection<HtmlFragment>, headers: Collection<HtmlFragment>,
connector: EngineConnectorConfig? = null,
visionFragment: HtmlVisionFragment, visionFragment: HtmlVisionFragment,
) { ) {
require(WebSockets) require(WebSockets)
val collector: MutableMap<Name, Vision> = mutableMapOf() val collector: MutableMap<Name, Vision> = mutableMapOf()
val html = createHTML().apply {
head {
meta {
charset = "utf-8"
}
headers.forEach { header ->
consumer.header()
}
}
body {
//Load the fragment and remember all loaded visions
visionFragment(
visionManager = configuration.visionManager,
embedData = configuration.dataMode == VisionRoute.Mode.EMBED,
fetchDataUrl = if (configuration.dataMode != VisionRoute.Mode.EMBED) {
url {
host = connector.host
port = connector.port
path(route, "data")
}
} else null,
updatesUrl = if (configuration.dataMode == VisionRoute.Mode.UPDATE) {
url {
protocol = URLProtocol.WS
host = connector.host
port = connector.port
path(route, "ws")
}
} else null,
onVisionRendered = { name, vision -> collector[name] = vision },
fragment = visionFragment
)
}
}.finalize()
//serve data //serve data
serveVisionData(configuration, collector) serveVisionData(configuration, collector)
//filled pages //filled pages
routing { routing {
get(route) { get(route) {
call.respondText(html, ContentType.Text.Html) val host = connector?.host ?: call.request.host()
val port = connector?.port ?: call.request.port()
call.respondHtml {
head {
meta {
charset = "utf-8"
}
headers.forEach { header ->
consumer.header()
}
}
body {
//Load the fragment and remember all loaded visions
visionFragment(
visionManager = configuration.visionManager,
embedData = configuration.dataMode == VisionRoute.Mode.EMBED,
fetchDataUrl = if (configuration.dataMode != VisionRoute.Mode.EMBED) {
url {
this.host = host
this.port = port
path(route, "data")
}
} else null,
updatesUrl = if (configuration.dataMode == VisionRoute.Mode.UPDATE) {
url {
protocol = URLProtocol.WS
this.host = host
this.port = port
path(route, "ws")
}
} else null,
onVisionRendered = { name, vision -> collector[name] = vision },
fragment = visionFragment
)
}
}
} }
} }
} }
public fun Application.visionPage( public fun Application.visionPage(
connector: EngineConnectorConfig,
visionManager: VisionManager, visionManager: VisionManager,
vararg headers: HtmlFragment, vararg headers: HtmlFragment,
route: String = "/", route: String = "/",
connector: EngineConnectorConfig? = null,
configurationBuilder: VisionRoute.() -> Unit = {}, configurationBuilder: VisionRoute.() -> Unit = {},
visionFragment: HtmlVisionFragment, visionFragment: HtmlVisionFragment,
) { ) {
val configuration = VisionRoute(route, visionManager).apply(configurationBuilder) val configuration = VisionRoute(route, visionManager).apply(configurationBuilder)
visionPage(route, configuration, connector, listOf(*headers), visionFragment) visionPage(route, configuration, listOf(*headers), connector, visionFragment)
} }
/** /**
* Render given [VisionPage] at server * Render given [VisionPage] at server
*/ */
public fun Application.visionPage( public fun Application.visionPage(
connector: EngineConnectorConfig,
page: VisionPage, page: VisionPage,
route: String = "/", route: String = "/",
connector: EngineConnectorConfig? = null,
configurationBuilder: VisionRoute.() -> Unit = {}, configurationBuilder: VisionRoute.() -> Unit = {},
) { ) {
val configuration = VisionRoute(route, page.visionManager).apply(configurationBuilder) val configuration = VisionRoute(route, page.visionManager).apply(configurationBuilder)
visionPage(route, configuration, connector, page.pageHeaders.values, visionFragment = page.content) visionPage(route, configuration, page.pageHeaders.values, connector, visionFragment = page.content)
} }

View File

@ -22,7 +22,8 @@ public fun <P : Pipeline<*, ApplicationCall>, B : Any, F : Any> P.require(
*/ */
public fun ApplicationEngine.openInBrowser() { public fun ApplicationEngine.openInBrowser() {
val connector = environment.connectors.first() val connector = environment.connectors.first()
val uri = URI("http", null, connector.host, connector.port, null, null, null) val host = if (connector.host == "0.0.0.0") "127.0.0.1" else connector.host
val uri = URI("http", null, host, connector.port, null, null, null)
Desktop.getDesktop().browse(uri) Desktop.getDesktop().browse(uri)
} }

View File

@ -11,10 +11,10 @@ import space.kscience.dataforge.context.Context
import space.kscience.dataforge.context.PluginFactory import space.kscience.dataforge.context.PluginFactory
import space.kscience.dataforge.context.PluginTag import space.kscience.dataforge.context.PluginTag
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.visionforge.* import space.kscience.visionforge.*
import space.kscience.visionforge.html.VisionOutput import space.kscience.visionforge.html.VisionOutput
import space.kscience.visionforge.solid.specifications.Canvas3DOptions
public class Solids(meta: Meta) : VisionPlugin(meta), MutableVisionContainer<Solid> { public class Solids(meta: Meta) : VisionPlugin(meta), MutableVisionContainer<Solid> {
@ -80,8 +80,10 @@ public class Solids(meta: Meta) : VisionPlugin(meta), MutableVisionContainer<Sol
} }
@VisionBuilder @VisionBuilder
@DFExperimental public inline fun VisionOutput.solid(options: Canvas3DOptions? = null, block: SolidGroup.() -> Unit): SolidGroup {
public inline fun VisionOutput.solid(block: SolidGroup.() -> Unit): SolidGroup {
requirePlugin(Solids) requirePlugin(Solids)
options?.let {
meta = options.meta
}
return SolidGroup().apply(block) return SolidGroup().apply(block)
} }

View File

@ -34,7 +34,6 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
objectFactories[Box::class] = ThreeBoxFactory objectFactories[Box::class] = ThreeBoxFactory
objectFactories[Convex::class] = ThreeConvexFactory objectFactories[Convex::class] = ThreeConvexFactory
objectFactories[Sphere::class] = ThreeSphereFactory objectFactories[Sphere::class] = ThreeSphereFactory
// objectFactories[ConeSegment::class] = ThreeConeFactory
objectFactories[PolyLine::class] = ThreeSmartLineFactory objectFactories[PolyLine::class] = ThreeSmartLineFactory
objectFactories[SolidLabel::class] = ThreeCanvasLabelFactory objectFactories[SolidLabel::class] = ThreeCanvasLabelFactory
objectFactories[AmbientLightSource::class] = ThreeAmbientLightFactory objectFactories[AmbientLightSource::class] = ThreeAmbientLightFactory
@ -143,7 +142,8 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
internal fun renderSolid( internal fun renderSolid(
element: Element, element: Element,
vision: Solid, vision: Solid,
): ThreeCanvas = getOrCreateCanvas(element).apply { options: Canvas3DOptions = Canvas3DOptions(),
): ThreeCanvas = getOrCreateCanvas(element, options).apply {
render(vision) render(vision)
} }
@ -151,9 +151,8 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
renderSolid( renderSolid(
element, element,
vision as? Solid ?: error("Solid expected but ${vision::class} found"), vision as? Solid ?: error("Solid expected but ${vision::class} found"),
).apply { Canvas3DOptions.read(meta)
options.meta.update(meta) )
}
} }
public companion object : PluginFactory<ThreePlugin> { public companion object : PluginFactory<ThreePlugin> {