Fully working file renderer.

This commit is contained in:
Alexander Nozik 2020-12-12 21:19:41 +03:00
parent 70ac2c99dd
commit 651a875eee
19 changed files with 266 additions and 129 deletions

View File

@ -1,64 +1,20 @@
plugins { plugins {
id("ru.mipt.npm.mpp") id("ru.mipt.npm.jvm")
application application
} }
group = "ru.mipt.npm" kscience {
useSerialization {
//val kvisionVersion: String = "3.16.2"
kscience{
useSerialization{
json() json()
} }
application() application()
} }
val ktorVersion: String by rootProject.extra group = "ru.mipt.npm"
kotlin { dependencies{
js{ implementation(project(":visionforge-threejs:visionforge-threejs-server"))
browser {
webpackTask {
this.outputFileName = "visionforge-solid.js"
}
}
}
afterEvaluate {
val jsBrowserDistribution by tasks.getting
jvm {
withJava()
compilations[org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.Companion.MAIN_COMPILATION_NAME]?.apply {
tasks.getByName<ProcessResources>(processResourcesTaskName) {
dependsOn(jsBrowserDistribution)
afterEvaluate {
from(jsBrowserDistribution)
}
}
}
}
}
sourceSets {
commonMain {
dependencies {
implementation(project(":visionforge-solid"))
}
}
jvmMain {
dependencies {
implementation(project(":visionforge-server"))
}
}
jsMain {
dependencies {
implementation(project(":visionforge-threejs"))
}
}
}
} }
application { application {

View File

@ -1,17 +0,0 @@
package ru.mipt.npm.sat
import hep.dataforge.context.Global
import hep.dataforge.vision.solid.SolidManager
import kotlin.test.Test
import kotlin.test.assertEquals
class GeometrySerializationTest {
@Test
fun testSerialization(){
val geometry = visionOfSatellite()
val manager = Global.plugins.fetch(SolidManager)
val string = manager.visionManager.encodeToString(geometry)
val reconstructed = manager.visionManager.decodeFromString(string)
assertEquals(geometry.config,reconstructed.config)
}
}

View File

@ -1,16 +0,0 @@
package ru.mipt.npm.sat
import hep.dataforge.context.Global
import hep.dataforge.vision.client.VisionClient
import hep.dataforge.vision.client.renderAllVisions
import hep.dataforge.vision.solid.three.ThreePlugin
import kotlinx.browser.window
fun main() {
//Loading three-js renderer
Global.plugins.load(ThreePlugin)
//Fetch from server and render visions for all outputs
window.onload = {
Global.plugins.fetch(VisionClient).renderAllVisions()
}
}

View File

@ -1,14 +1,11 @@
package ru.mipt.npm.sat package ru.mipt.npm.sat
import hep.dataforge.context.Global
import hep.dataforge.names.toName import hep.dataforge.names.toName
import hep.dataforge.vision.server.*
import hep.dataforge.vision.solid.Solid import hep.dataforge.vision.solid.Solid
import hep.dataforge.vision.solid.SolidManager
import hep.dataforge.vision.solid.color import hep.dataforge.vision.solid.color
import hep.dataforge.vision.visionManager import hep.dataforge.vision.three.server.*
import io.ktor.util.KtorExperimentalAPI import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -16,22 +13,13 @@ import kotlinx.html.div
import kotlinx.html.h1 import kotlinx.html.h1
import kotlin.random.Random import kotlin.random.Random
@OptIn(KtorExperimentalAPI::class)
fun main() { fun main() {
//Create a geometry //Create a geometry
val sat = visionOfSatellite(ySegments = 3) val sat = visionOfSatellite(ySegments = 3)
val context = Global.context("SAT") {
//need to install solids extension, vision manager is installed automatically
plugin(SolidManager)
}
// fetch vision manager
val visionManager = context.visionManager
val server = visionManager.serve { val server = visionManager.serve {
//use client library //use client library
useScript("visionforge-solid.js") useThreeJs()
//use css //use css
useCss("css/styles.css") useCss("css/styles.css")
page { page {
@ -44,9 +32,12 @@ fun main() {
server.show() server.show()
context.launch { GlobalScope.launch {
while (isActive) { while (isActive) {
val target = "layer[${Random.nextInt(1, 11)}].segment[${Random.nextInt(3)},${Random.nextInt(3)}]".toName() val randomLayer = Random.nextInt(1, 11)
val randomI = Random.nextInt(1, 4)
val randomJ = Random.nextInt(1, 4)
val target = "layer[$randomLayer].segment[$randomI,$randomJ]".toName()
(sat[target] as? Solid)?.color("red") (sat[target] as? Solid)?.color("red")
delay(300) delay(300)
(sat[target] as? Solid)?.color = "green" (sat[target] as? Solid)?.color = "green"
@ -54,7 +45,7 @@ fun main() {
} }
} }
println("Press Enter to close server") println("Enter 'exit' to close server")
while (readLine() != "exit") { while (readLine() != "exit") {
// //
} }

View File

@ -12,7 +12,14 @@ repositories{
} }
kotlin { kotlin {
jvm() jvm{
compilations.all {
kotlinOptions.jvmTarget = "11"
}
testRuns["test"].executionTask.configure {
useJUnitPlatform()
}
}
js(IR) { js(IR) {
browser { browser {
} }
@ -36,6 +43,7 @@ kotlin {
val jvmMain by getting{ val jvmMain by getting{
dependencies { dependencies {
implementation("com.github.Ricky12Awesome:json-schema-serialization:0.6.6") implementation("com.github.Ricky12Awesome:json-schema-serialization:0.6.6")
implementation(project(":visionforge-threejs:visionforge-threejs-server"))
} }
} }
} }

View File

@ -0,0 +1,19 @@
package ru.mipt.npm.sat
import hep.dataforge.vision.VisionManager
import hep.dataforge.vision.html.fragment
import hep.dataforge.vision.solid.box
import hep.dataforge.vision.three.server.makeFile
import hep.dataforge.vision.three.server.solid
fun main() {
val fragment = VisionManager.fragment {
vision("canvas") {
solid {
box(100, 100, 100)
}
}
}
fragment.makeFile()
}

View File

@ -40,6 +40,7 @@ include(
":visionforge-solid", ":visionforge-solid",
":visionforge-fx", ":visionforge-fx",
":visionforge-threejs", ":visionforge-threejs",
":visionforge-threejs:visionforge-threejs-server",
":visionforge-gdml", ":visionforge-gdml",
":visionforge-server", ":visionforge-server",
":demo:spatial-showcase", ":demo:spatial-showcase",

View File

@ -110,23 +110,21 @@ public fun Vision.flowChanges(
manager: VisionManager, manager: VisionManager,
collectionDuration: Duration, collectionDuration: Duration,
): Flow<VisionChange> = flow { ): Flow<VisionChange> = flow {
supervisorScope { val mutex = Mutex()
val mutex = Mutex()
var collector = VisionChangeBuilder() var collector = VisionChangeBuilder()
collectChange(Name.EMPTY, this@flowChanges, mutex) { collector } manager.context.collectChange(Name.EMPTY, this@flowChanges, mutex) { collector }
while (currentCoroutineContext().isActive) { while (currentCoroutineContext().isActive) {
//Wait for changes to accumulate //Wait for changes to accumulate
delay(collectionDuration) delay(collectionDuration)
//Propagate updates only if something is changed //Propagate updates only if something is changed
if (!collector.isEmpty()) { if (!collector.isEmpty()) {
//emit changes //emit changes
mutex.withLock { mutex.withLock {
emit(collector.isolate(manager)) emit(collector.isolate(manager))
//Reset the collector //Reset the collector
collector = VisionChangeBuilder() collector = VisionChangeBuilder()
}
} }
} }
} }

View File

@ -1,5 +1,7 @@
package hep.dataforge.vision.html package hep.dataforge.vision.html
import hep.dataforge.meta.DFExperimental
import hep.dataforge.vision.VisionManager
import kotlinx.html.FlowContent import kotlinx.html.FlowContent
import kotlinx.html.TagConsumer import kotlinx.html.TagConsumer
@ -14,3 +16,6 @@ public fun FlowContent.fragment(fragment: HtmlFragment) {
} }
public typealias HtmlVisionFragment = VisionTagConsumer<*>.() -> Unit public typealias HtmlVisionFragment = VisionTagConsumer<*>.() -> Unit
@DFExperimental
public fun VisionManager.Companion.fragment(content: HtmlVisionFragment): VisionTagConsumer<*>.() -> Unit = content

View File

@ -1,5 +1,7 @@
package hep.dataforge.vision package hep.dataforge.vision
import hep.dataforge.context.Context
import hep.dataforge.meta.DFExperimental
import hep.dataforge.vision.html.HtmlFragment import hep.dataforge.vision.html.HtmlFragment
import kotlinx.html.link import kotlinx.html.link
import kotlinx.html.script import kotlinx.html.script
@ -33,6 +35,8 @@ public enum class ResourceLocation {
EMBED EMBED
} }
internal const val DATAFORGE_ASSETS_PATH = ".dataforge/assets"
/** /**
* Check if the asset exists in given local location and put it there if it does not * Check if the asset exists in given local location and put it there if it does not
@ -62,14 +66,11 @@ internal fun checkOrStoreFile(basePath: Path, filePath: Path, resource: String):
* A header that automatically copies relevant scripts to given path * A header that automatically copies relevant scripts to given path
*/ */
internal fun fileScriptHeader( internal fun fileScriptHeader(
basePath: Path, path: Path,
scriptPath: Path,
resource: String
): HtmlFragment = { ): HtmlFragment = {
val relativePath = checkOrStoreFile(basePath, scriptPath, resource)
script { script {
type = "text/javascript" type = "text/javascript"
src = relativePath.toString() src = path.toString()
} }
} }
@ -86,7 +87,7 @@ internal fun embedScriptHeader(resource: String): HtmlFragment = {
internal fun fileCssHeader( internal fun fileCssHeader(
basePath: Path, basePath: Path,
cssPath: Path, cssPath: Path,
resource: String resource: String,
): HtmlFragment = { ): HtmlFragment = {
val relativePath = checkOrStoreFile(basePath, cssPath, resource) val relativePath = checkOrStoreFile(basePath, cssPath, resource)
link { link {
@ -95,6 +96,37 @@ internal fun fileCssHeader(
} }
} }
/**
* Make a script header, automatically copying file to appropriate location
*/
@DFExperimental
public fun Context.Companion.scriptHeader(
scriptResource: String,
basePath: Path?,
resourceLocation: ResourceLocation,
): HtmlFragment {
val targetPath = if (basePath == null) null else {
when (resourceLocation) {
ResourceLocation.LOCAL -> checkOrStoreFile(
basePath,
Path.of(DATAFORGE_ASSETS_PATH),
scriptResource
)
ResourceLocation.SYSTEM -> checkOrStoreFile(
Path.of("."),
Path.of(System.getProperty("user.home")).resolve(DATAFORGE_ASSETS_PATH),
scriptResource
)
ResourceLocation.EMBED -> null
}
}
return if (targetPath == null) {
embedScriptHeader(scriptResource)
} else {
fileScriptHeader(targetPath)
}
}
// //
///** ///**
// * A system-wide plotly store location // * A system-wide plotly store location

View File

@ -1,5 +1,6 @@
package hep.dataforge.vision package hep.dataforge.vision
import hep.dataforge.meta.DFExperimental
import hep.dataforge.vision.html.* import hep.dataforge.vision.html.*
import kotlinx.html.* import kotlinx.html.*
import kotlinx.html.stream.createHTML import kotlinx.html.stream.createHTML
@ -8,11 +9,17 @@ import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
/** /**
* Make a file with the embedded vision data * Make a file with the embedded vision data
*/ */
public fun HtmlVisionFragment.makeFile(manager: VisionManager, vararg headers: HtmlFragment, path: Path? = null, show: Boolean = true) { @DFExperimental
public fun HtmlVisionFragment.makeFile(
manager: VisionManager,
vararg headers: HtmlFragment,
path: Path? = null,
title: String = "VisionForge page",
show: Boolean = true,
) {
val actualFile = path ?: Files.createTempFile("tempPlot", ".html") val actualFile = path ?: Files.createTempFile("tempPlot", ".html")
Files.createDirectories(actualFile.parent) Files.createDirectories(actualFile.parent)
val htmlString = createHTML().apply { val htmlString = createHTML().apply {

View File

@ -1,4 +1,4 @@
package hep.dataforge.vision.server package hep.dataforge.vision.three.server
import hep.dataforge.context.Context import hep.dataforge.context.Context
import hep.dataforge.meta.* import hep.dataforge.meta.*
@ -12,7 +12,7 @@ import hep.dataforge.vision.html.HtmlFragment
import hep.dataforge.vision.html.HtmlVisionFragment import hep.dataforge.vision.html.HtmlVisionFragment
import hep.dataforge.vision.html.VisionTagConsumer import hep.dataforge.vision.html.VisionTagConsumer
import hep.dataforge.vision.html.fragment import hep.dataforge.vision.html.fragment
import hep.dataforge.vision.server.VisionServer.Companion.DEFAULT_PAGE import hep.dataforge.vision.three.server.VisionServer.Companion.DEFAULT_PAGE
import io.ktor.application.* import io.ktor.application.*
import io.ktor.features.CORS import io.ktor.features.CORS
import io.ktor.features.CallLogging import io.ktor.features.CallLogging

View File

@ -134,7 +134,9 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
} }
override fun render(element: Element, vision: Vision, meta: Meta) { override fun render(element: Element, vision: Vision, meta: Meta) {
createCanvas(element, Canvas3DOptions.read(meta)).render(vision as? Solid ?: error("Only solids are rendered")) createCanvas(element, Canvas3DOptions.read(meta)).render(
vision as? Solid ?: error("Solid expected but ${vision::class} is found")
)
} }
public companion object : PluginFactory<ThreePlugin> { public companion object : PluginFactory<ThreePlugin> {

View File

@ -0,0 +1,52 @@
plugins {
id("ru.mipt.npm.mpp")
}
kscience{
useSerialization{
json()
}
}
val ktorVersion: String by rootProject.extra
kotlin {
js{
browser {
webpackTask {
this.outputFileName = "js/visionforge-three.js"
}
}
binaries.executable()
}
afterEvaluate {
val jsBrowserDistribution by tasks.getting
tasks.getByName<ProcessResources>("jvmProcessResources") {
dependsOn(jsBrowserDistribution)
afterEvaluate {
from(jsBrowserDistribution)
}
}
}
sourceSets {
commonMain {
dependencies {
api(project(":visionforge-solid"))
}
}
jvmMain {
dependencies {
api(project(":visionforge-server"))
}
}
jsMain {
dependencies {
api(project(":visionforge-threejs"))
}
}
}
}

View File

@ -0,0 +1,8 @@
package hep.dataforge.vision.three.server
import hep.dataforge.context.Context
import hep.dataforge.vision.VisionManager
public expect val visionContext: Context
public val visionManager: VisionManager get() = visionContext.plugins.fetch(VisionManager)

View File

@ -0,0 +1,48 @@
package hep.dataforge.vision.three.server
import hep.dataforge.context.Context
import hep.dataforge.context.Global
import hep.dataforge.vision.client.VisionClient
import hep.dataforge.vision.client.renderAllVisions
import hep.dataforge.vision.solid.three.ThreePlugin
import kotlinx.browser.window
//FIXME check plugin loading in JS
//public actual val visionContext: Context = Global.context("vision-client") {
// //Loading three-js renderer
// plugin(ThreePlugin)
//}
public actual val visionContext: Context = Global.context("vision-client").apply {
//Loading three-js renderer
plugins.fetch(ThreePlugin)
}
public val clientManager: VisionClient get() = visionContext.plugins.fetch(VisionClient)
///**
// * Render all visions in the document using registered renderers
// */
//@JsExport
//public fun renderVisions() {
// //Fetch from server and render visions for all outputs
// window.onload = {
// clientManager.renderAllVisions()
// }
//}
//
///**
// * Render all visions in a given element, using registered renderers
// */
//@JsExport
//public fun renderAllVisionsAt(element: Element) {
// clientManager.renderAllVisionsAt(element)
//}
public fun main() {
//Fetch from server and render visions for all outputs
window.onload = {
clientManager.renderAllVisions()
}
}

View File

@ -0,0 +1,43 @@
package hep.dataforge.vision.three.server
import hep.dataforge.context.Context
import hep.dataforge.context.Global
import hep.dataforge.meta.DFExperimental
import hep.dataforge.vision.ResourceLocation
import hep.dataforge.vision.html.HtmlVisionFragment
import hep.dataforge.vision.html.VisionOutput
import hep.dataforge.vision.makeFile
import hep.dataforge.vision.scriptHeader
import hep.dataforge.vision.solid.SolidGroup
import hep.dataforge.vision.solid.SolidManager
import java.nio.file.Path
public actual val visionContext: Context = Global.context("vision-server") {
//Loading solid manager for the backend (it does not know about three
plugin(SolidManager)
}
public fun VisionServer.useThreeJs(): Unit {
useScript("js/visionforge-three.js")
// header {
// script {
// unsafe {
// +"renderThreeVisions()"
// }
// }
// }
}
@DFExperimental
public inline fun VisionOutput.solid(block: SolidGroup.() -> Unit): SolidGroup = SolidGroup().apply(block)
@OptIn(DFExperimental::class)
public fun HtmlVisionFragment.makeFile(
path: Path? = null,
title: String = "VisionForge page",
resourceLocation: ResourceLocation = ResourceLocation.SYSTEM,
show: Boolean = true,
) {
val scriptHeader = Context.scriptHeader("/js/visionforge-three.js", path, resourceLocation)
makeFile(visionManager, path = path, show = show, title = title, headers = arrayOf(scriptHeader))
}