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 {
group = "space.kscience"
version = "0.3.0-dev-8"
version = "0.3.0-dev-9"
}
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 {
//
// sourceSets {

View File

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

View File

@ -33,6 +33,7 @@ fun main() = makeVisionFile {
cylinder(30,20, name = "cylinder"){
detail = 31
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.visionforge.Colors
import space.kscience.visionforge.html.VisionPage
import space.kscience.visionforge.server.EngineConnectorConfig
import space.kscience.visionforge.server.close
import space.kscience.visionforge.server.openInBrowser
import space.kscience.visionforge.server.visionPage
@ -23,6 +22,7 @@ import space.kscience.visionforge.three.threeJsHeader
import kotlin.random.Random
@Suppress("ExtractKtorModule")
fun main() {
val satContext = Context("sat") {
plugin(Solids)
@ -36,15 +36,12 @@ fun main() {
color.set(Colors.white)
}
}
val connector = EngineConnectorConfig("localhost", 7777)
val server = embeddedServer(CIO, connector.port, connector.host) {
val server = embeddedServer(CIO, port = 7777) {
routing {
staticResources("", null, null)
}
visionPage(
connector,
solids.visionManager, VisionPage.threeJsHeader,
VisionPage.styleSheetHeader("css/styles.css")
) {

View File

@ -1,6 +1,5 @@
plugins {
id("space.kscience.gradle.mpp")
`maven-publish`
application
}
@ -22,8 +21,4 @@ kscience {
application {
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.withContext
import kotlinx.html.*
import kotlinx.html.stream.createHTML
import space.kscience.dataforge.context.Context
import space.kscience.dataforge.context.ContextAware
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")
launch {
for(frame in incoming) {
for (frame in incoming) {
val data = frame.data.decodeToString()
application.log.debug("Received update for $name: \n$data")
val change = configuration.visionManager.jsonFormat.decodeFromString(
@ -151,82 +150,82 @@ public fun Application.serveVisionData(
public fun Application.visionPage(
route: String,
configuration: VisionRoute,
connector: EngineConnectorConfig,
headers: Collection<HtmlFragment>,
connector: EngineConnectorConfig? = null,
visionFragment: HtmlVisionFragment,
) {
require(WebSockets)
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
serveVisionData(configuration, collector)
//filled pages
routing {
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(
connector: EngineConnectorConfig,
visionManager: VisionManager,
vararg headers: HtmlFragment,
route: String = "/",
connector: EngineConnectorConfig? = null,
configurationBuilder: VisionRoute.() -> Unit = {},
visionFragment: HtmlVisionFragment,
) {
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
*/
public fun Application.visionPage(
connector: EngineConnectorConfig,
page: VisionPage,
route: String = "/",
connector: EngineConnectorConfig? = null,
configurationBuilder: VisionRoute.() -> Unit = {},
) {
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() {
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)
}

View File

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

View File

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