Fix after dev merge.

This commit is contained in:
Alexander Nozik 2021-01-12 12:52:22 +03:00
parent 33146fef1b
commit 0c1d6139ae
45 changed files with 511 additions and 304 deletions

View File

@ -1,9 +1,9 @@
package hep.dataforge.vision.gdml.demo package hep.dataforge.vision.gdml.demo
import hep.dataforge.Application
import hep.dataforge.context.Global import hep.dataforge.context.Global
import hep.dataforge.startApplication import hep.dataforge.vision.Application
import hep.dataforge.vision.gdml.toVision import hep.dataforge.vision.gdml.toVision
import hep.dataforge.vision.startApplication
import kotlinx.browser.document import kotlinx.browser.document
import react.child import react.child
import react.dom.render import react.dom.render

View File

@ -1,8 +1,8 @@
package ru.mipt.npm.muon.monitor package ru.mipt.npm.muon.monitor
import hep.dataforge.Application
import hep.dataforge.context.Global import hep.dataforge.context.Global
import hep.dataforge.startApplication import hep.dataforge.vision.Application
import hep.dataforge.vision.startApplication
import io.ktor.client.HttpClient import io.ktor.client.HttpClient
import io.ktor.client.features.json.JsonFeature import io.ktor.client.features.json.JsonFeature
import io.ktor.client.features.json.serializer.KotlinxSerializer import io.ktor.client.features.json.serializer.KotlinxSerializer

View File

@ -4,7 +4,7 @@ import hep.dataforge.meta.Meta
import hep.dataforge.meta.invoke import hep.dataforge.meta.invoke
import hep.dataforge.names.toName import hep.dataforge.names.toName
import hep.dataforge.vision.Colors import hep.dataforge.vision.Colors
import hep.dataforge.vision.layout.Page import hep.dataforge.vision.VisionLayout
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,12 +15,12 @@ import kotlin.math.sin
import kotlin.random.Random import kotlin.random.Random
fun Page<Solid>.demo(name: String, title: String = name, block: SolidGroup.() -> Unit) { fun VisionLayout<Solid>.demo(name: String, title: String = name, block: SolidGroup.() -> Unit) {
val meta = Meta { val meta = Meta {
"title" put title "title" put title
} }
val output = output(name.toName(), meta)?: error("Output with name $name not found") val vision = SolidGroup(block)
output.solidGroup (builder = block) render(name.toName(), vision)
} }
val canvasOptions = Canvas3DOptions { val canvasOptions = Canvas3DOptions {
@ -35,7 +35,7 @@ val canvasOptions = Canvas3DOptions {
} }
} }
fun Page<Solid>.showcase() { fun VisionLayout<Solid>.showcase() {
demo("shapes", "Basic shapes") { demo("shapes", "Basic shapes") {
box(100.0, 100.0, 100.0) { box(100.0, 100.0, 100.0) {
z = -110.0 z = -110.0
@ -136,7 +136,7 @@ fun Page<Solid>.showcase() {
} }
} }
fun Page<Solid>.showcaseCSG() { fun VisionLayout<Solid>.showcaseCSG() {
demo("CSG.simple", "CSG operations") { demo("CSG.simple", "CSG operations") {
composite(CompositeType.INTERSECT) { composite(CompositeType.INTERSECT) {
y = 300 y = 300

View File

@ -1,9 +1,9 @@
package hep.dataforge.vision.solid.demo package hep.dataforge.vision.solid.demo
import hep.dataforge.Application import hep.dataforge.vision.Application
import hep.dataforge.startApplication
import hep.dataforge.vision.solid.x import hep.dataforge.vision.solid.x
import hep.dataforge.vision.solid.y import hep.dataforge.vision.solid.y
import hep.dataforge.vision.startApplication
import kotlinx.browser.document import kotlinx.browser.document
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay import kotlinx.coroutines.delay

View File

@ -5,8 +5,7 @@ import hep.dataforge.meta.Meta
import hep.dataforge.meta.get import hep.dataforge.meta.get
import hep.dataforge.meta.string import hep.dataforge.meta.string
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.vision.layout.Output import hep.dataforge.vision.VisionLayout
import hep.dataforge.vision.layout.Page
import hep.dataforge.vision.solid.Solid import hep.dataforge.vision.solid.Solid
import hep.dataforge.vision.solid.three.ThreeCanvas import hep.dataforge.vision.solid.three.ThreeCanvas
import hep.dataforge.vision.solid.three.ThreePlugin import hep.dataforge.vision.solid.three.ThreePlugin
@ -20,7 +19,7 @@ import org.w3c.dom.Element
import org.w3c.dom.HTMLDivElement import org.w3c.dom.HTMLDivElement
import org.w3c.dom.HTMLElement import org.w3c.dom.HTMLElement
class ThreeDemoGrid(element: Element) : Page<Solid> { class ThreeDemoGrid(element: Element) : VisionLayout<Solid> {
private lateinit var navigationElement: HTMLElement private lateinit var navigationElement: HTMLElement
private lateinit var contentElement: HTMLDivElement private lateinit var contentElement: HTMLDivElement
@ -47,9 +46,8 @@ class ThreeDemoGrid(element: Element) : Page<Solid> {
} }
} }
override fun render(name: Name, vision: Solid, meta: Meta) {
@Suppress("UNCHECKED_CAST") outputs.getOrPut(name) {
override fun output(name: Name, meta: Meta): Output<Solid> = outputs.getOrPut(name) {
navigationElement.append { navigationElement.append {
li("nav-item") { li("nav-item") {
a(classes = "nav-link") { a(classes = "nav-link") {
@ -72,6 +70,7 @@ class ThreeDemoGrid(element: Element) : Page<Solid> {
} }
val element = document.getElementById("output-$name") ?: error("Element not found") val element = document.getElementById("output-$name") ?: error("Element not found")
three.createCanvas(element, canvasOptions) three.createCanvas(element, canvasOptions)
}.render(vision)
} }
} }

View File

@ -3,8 +3,7 @@ package hep.dataforge.vision.solid.demo
import hep.dataforge.context.Global import hep.dataforge.context.Global
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.vision.layout.Output import hep.dataforge.vision.VisionLayout
import hep.dataforge.vision.layout.Page
import hep.dataforge.vision.solid.FX3DPlugin import hep.dataforge.vision.solid.FX3DPlugin
import hep.dataforge.vision.solid.FXCanvas3D import hep.dataforge.vision.solid.FXCanvas3D
import hep.dataforge.vision.solid.Solid import hep.dataforge.vision.solid.Solid
@ -13,7 +12,7 @@ import javafx.scene.Parent
import javafx.scene.control.Tab import javafx.scene.control.Tab
import tornadofx.* import tornadofx.*
class FXDemoGrid : View(title = "DataForge-vis FX demo"), Page<Solid> { class FXDemoGrid : View(title = "DataForge-vis FX demo"), VisionLayout<Solid> {
private val outputs = FXCollections.observableHashMap<Name, FXCanvas3D>() private val outputs = FXCollections.observableHashMap<Name, FXCanvas3D>()
override val root: Parent = borderpane { override val root: Parent = borderpane {
@ -26,8 +25,8 @@ class FXDemoGrid : View(title = "DataForge-vis FX demo"), Page<Solid> {
private val fx3d = Global.plugins.fetch(FX3DPlugin) private val fx3d = Global.plugins.fetch(FX3DPlugin)
override fun output(name: Name, meta: Meta): Output<Solid> = outputs.getOrPut(name) { override fun render(name: Name, vision: Solid, meta: Meta) {
FXCanvas3D(fx3d, canvasOptions) outputs.getOrPut(name) { FXCanvas3D(fx3d, canvasOptions) }.render(vision)
} }
} }

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

View File

@ -1,27 +1,9 @@
import hep.dataforge.context.Context import hep.dataforge.vision.plotly.withPlotly
import hep.dataforge.context.Global import hep.dataforge.vision.renderVisionsInWindow
import hep.dataforge.meta.DFExperimental import hep.dataforge.vision.solid.three.loadThreeJs
import hep.dataforge.vision.client.VisionClient
import hep.dataforge.vision.client.renderAllVisions
import hep.dataforge.vision.plotly.PlotlyPlugin
import hep.dataforge.vision.solid.three.ThreePlugin
import kotlinx.browser.window
@DFExperimental
fun main() { fun main() {
withPlotly()
val visionContext: Context = Global.context("VISION") { loadThreeJs()
plugin(ThreePlugin) renderVisionsInWindow()
plugin(PlotlyPlugin)
plugin(VisionClient)
}
//Loading three-js renderer
val clientManager = visionContext.plugins.fetch(VisionClient)
//Fetch from server and render visions for all outputs
window.onload = {
clientManager.renderAllVisions()
}
//startApplication(::PlayGroundApp)
} }

View File

@ -1,6 +1,8 @@
package hep.dataforge.vision.solid package hep.dataforge.vision.examples
import com.github.ricky12awesome.jss.encodeToSchema import com.github.ricky12awesome.jss.encodeToSchema
import hep.dataforge.vision.solid.SolidGroup
import hep.dataforge.vision.solid.SolidManager
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
fun main() { fun main() {

View File

@ -1,38 +0,0 @@
package hep.dataforge.vision.solid
import hep.dataforge.context.Context
import hep.dataforge.context.Global
import hep.dataforge.meta.DFExperimental
import hep.dataforge.vision.ResourceLocation
import hep.dataforge.vision.VisionManager
import hep.dataforge.vision.html.HtmlVisionFragment
import hep.dataforge.vision.makeVisionFile
import hep.dataforge.vision.scriptHeader
import hep.dataforge.vision.three.server.VisionServer
import hep.dataforge.vision.three.server.useScript
import java.nio.file.Path
/**
* A global vision context used to resolve different vision renderers
*/
@DFExperimental
public val VisionForge: Context = Global.context("VISION") {
plugin(VisionManager)
plugin(SolidManager)
}
public fun VisionServer.usePlayground(): Unit {
useScript("js/visionforge-playground.js")
}
@DFExperimental
public fun Context.makeVisionFile(
fragment: HtmlVisionFragment,
path: Path? = null,
title: String = "VisionForge page",
resourceLocation: ResourceLocation = ResourceLocation.SYSTEM,
show: Boolean = true,
): Unit = makeVisionFile(fragment, path = path, title = title, show = show) { actualPath ->
scriptHeader("js/visionforge-playground.js", actualPath, resourceLocation)
}

View File

@ -1,19 +0,0 @@
package hep.dataforge.vision.solid
import hep.dataforge.meta.DFExperimental
import hep.dataforge.vision.ResourceLocation
import hep.dataforge.vision.VisionManager
import hep.dataforge.vision.html.fragment
@OptIn(DFExperimental::class)
fun main() {
val fragment = VisionManager.fragment {
vision("canvas") {
solid {
box(100, 100, 100)
}
}
}
VisionForge.makeVisionFile(fragment = fragment, resourceLocation = ResourceLocation.SYSTEM)
}

View File

@ -0,0 +1,25 @@
package hep.dataforge.vision.examples
import hep.dataforge.meta.DFExperimental
import hep.dataforge.vision.VisionForge
import hep.dataforge.vision.VisionManager
import hep.dataforge.vision.html.fragment
import hep.dataforge.vision.plotly.plotly
import hep.dataforge.vision.plotly.withPlotly
import kscience.plotly.scatter
@DFExperimental
fun main() {
val fragment = VisionManager.fragment {
vision {
plotly {
scatter {
x(1,2,3)
y(5,8,7)
}
}
}
}
VisionForge.withPlotly().makeVisionFile(fragment)
}

View File

@ -1,9 +1,11 @@
package hep.dataforge.vision.solid package hep.dataforge.vision.examples
import hep.dataforge.meta.DFExperimental import hep.dataforge.meta.DFExperimental
import hep.dataforge.vision.ResourceLocation import hep.dataforge.vision.VisionForge
import hep.dataforge.vision.VisionManager import hep.dataforge.vision.VisionManager
import hep.dataforge.vision.html.ResourceLocation
import hep.dataforge.vision.html.fragment import hep.dataforge.vision.html.fragment
import hep.dataforge.vision.solid.*
import kotlinx.html.h1 import kotlinx.html.h1
import java.nio.file.Paths import java.nio.file.Paths
import kotlin.random.Random import kotlin.random.Random

View File

@ -0,0 +1,35 @@
package hep.dataforge.vision.examples
import hep.dataforge.context.Context
import hep.dataforge.meta.DFExperimental
import hep.dataforge.vision.html.HtmlVisionFragment
import hep.dataforge.vision.html.ResourceLocation
import hep.dataforge.vision.html.scriptHeader
import hep.dataforge.vision.makeFile
import hep.dataforge.vision.page
import hep.dataforge.vision.three.server.VisionServer
import hep.dataforge.vision.three.server.useScript
import java.awt.Desktop
import java.nio.file.Path
public fun VisionServer.usePlayground(): Unit {
useScript("js/visionforge-playground.js")
}
@DFExperimental
public fun Context.makeVisionFile(
content: HtmlVisionFragment,
path: Path? = null,
title: String = "VisionForge page",
resourceLocation: ResourceLocation = ResourceLocation.SYSTEM,
show: Boolean = true,
): Unit {
val actualPath = page(title, content).makeFile(path) { actualPath ->
mapOf("threeJs" to scriptHeader("js/visionforge-playground.js", actualPath, resourceLocation))
}
if (show) Desktop.getDesktop().browse(actualPath.toFile().toURI())
}
// makeVisionFile(fragment, path = path, title = title, show = show) { actualPath ->
// scriptHeader("js/visionforge-playground.js", actualPath, resourceLocation)
//}

View File

@ -0,0 +1,22 @@
package hep.dataforge.vision.examples
import hep.dataforge.meta.DFExperimental
import hep.dataforge.vision.VisionForge
import hep.dataforge.vision.VisionManager
import hep.dataforge.vision.html.ResourceLocation
import hep.dataforge.vision.html.fragment
import hep.dataforge.vision.solid.box
import hep.dataforge.vision.solid.solid
@OptIn(DFExperimental::class)
fun main() {
val content = VisionManager.fragment {
vision("canvas") {
solid {
box(100, 100, 100)
}
}
}
VisionForge.makeVisionFile(content, resourceLocation = ResourceLocation.SYSTEM)
}

View File

@ -0,0 +1,5 @@
package hep.dataforge.vision
import hep.dataforge.context.Context
public expect val VisionForge: Context

View File

@ -0,0 +1,8 @@
package hep.dataforge.vision
import hep.dataforge.meta.Meta
import hep.dataforge.names.Name
public interface VisionLayout<in V: Vision> {
public fun render(name: Name, vision: V, meta: Meta = Meta.EMPTY)
}

View File

@ -3,6 +3,8 @@ package hep.dataforge.vision
import hep.dataforge.context.* import hep.dataforge.context.*
import hep.dataforge.meta.* import hep.dataforge.meta.*
import hep.dataforge.meta.descriptors.NodeDescriptor import hep.dataforge.meta.descriptors.NodeDescriptor
import hep.dataforge.names.Name
import hep.dataforge.names.toName
import kotlinx.serialization.PolymorphicSerializer import kotlinx.serialization.PolymorphicSerializer
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonElement
@ -75,6 +77,18 @@ public class VisionManager(meta: Meta) : AbstractPlugin(meta) {
} }
} }
public abstract class VisionPlugin(meta: Meta = Meta.EMPTY) : AbstractPlugin(meta) {
public val visionManager: VisionManager by require(VisionManager)
protected abstract val visionSerializersModule: SerializersModule
override fun content(target: String): Map<Name, Any> = when (target) {
VisionManager.VISION_SERIALIZER_MODULE_TARGET -> mapOf(tag.toString().toName() to visionSerializersModule)
else -> super.content(target)
}
}
/** /**
* Fetch a [VisionManager] from this plugin * Fetch a [VisionManager] from this plugin
*/ */

View File

@ -0,0 +1,26 @@
package hep.dataforge.vision.html
import hep.dataforge.context.Context
import kotlinx.html.*
public data class Page(
public val context: Context,
public val title: String,
public val headers: Map<String, HtmlFragment>,
public val content: HtmlVisionFragment
) {
public fun <R> render(root: TagConsumer<R>): R = root.apply {
head {
meta {
charset = "utf-8"
headers.values.forEach {
fragment(it)
}
}
title(title)
}
body {
embedVisionFragment(context.visionManager, fragment = content)
}
}.finalize()
}

View File

@ -4,19 +4,18 @@ import hep.dataforge.meta.Meta
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.vision.Vision import hep.dataforge.vision.Vision
import hep.dataforge.vision.VisionManager import hep.dataforge.vision.VisionManager
import kotlinx.html.DIV import kotlinx.html.*
import kotlinx.html.FlowContent
import kotlinx.html.script
import kotlinx.html.unsafe
public fun FlowContent.embedVisionFragment( public fun TagConsumer<*>.embedVisionFragment(
manager: VisionManager, manager: VisionManager,
idPrefix: String? = null, idPrefix: String? = null,
fragment: HtmlVisionFragment, fragment: HtmlVisionFragment,
) { ): Map<Name, Vision> {
val consumer = object : VisionTagConsumer<Any?>(consumer, idPrefix) { val visionMap = HashMap<Name, Vision>()
val consumer = object : VisionTagConsumer<Any?>(this@embedVisionFragment, idPrefix) {
override fun DIV.renderVision(name: Name, vision: Vision, outputMeta: Meta) { override fun DIV.renderVision(name: Name, vision: Vision, outputMeta: Meta) {
visionMap[name] = vision
script { script {
type = "text/json" type = "text/json"
attributes["class"] = OUTPUT_DATA_CLASS attributes["class"] = OUTPUT_DATA_CLASS
@ -27,17 +26,29 @@ public fun FlowContent.embedVisionFragment(
} }
} }
fragment(consumer) fragment(consumer)
return visionMap
} }
public fun FlowContent.embedVisionFragment(
manager: VisionManager,
idPrefix: String? = null,
fragment: HtmlVisionFragment,
): Map<Name, Vision> = consumer.embedVisionFragment(manager, idPrefix, fragment)
public typealias HtmlVisionRenderer = FlowContent.(name: Name, vision: Vision, meta: Meta) -> Unit public typealias HtmlVisionRenderer = FlowContent.(name: Name, vision: Vision, meta: Meta) -> Unit
public fun <R> FlowContent.renderVisionFragment( public fun FlowContent.renderVisionFragment(
renderer: DIV.(name: Name, vision: Vision, meta: Meta) -> Unit, renderer: DIV.(name: Name, vision: Vision, meta: Meta) -> Unit,
idPrefix: String? = null, idPrefix: String? = null,
fragment: HtmlVisionFragment, fragment: HtmlVisionFragment,
) { ): Map<Name, Vision> {
val visionMap = HashMap<Name, Vision>()
val consumer = object : VisionTagConsumer<Any?>(consumer, idPrefix) { val consumer = object : VisionTagConsumer<Any?>(consumer, idPrefix) {
override fun DIV.renderVision(name: Name, vision: Vision, outputMeta: Meta) = renderer(name, vision, outputMeta) override fun DIV.renderVision(name: Name, vision: Vision, outputMeta: Meta) {
visionMap[name] = vision
renderer(name, vision, outputMeta)
}
} }
fragment(consumer) fragment(consumer)
return visionMap
} }

View File

@ -1,16 +0,0 @@
package hep.dataforge.vision.layout
import hep.dataforge.meta.Meta
import hep.dataforge.names.Name
import hep.dataforge.vision.Vision
public fun interface Output<in V : Vision> {
public fun render(vision: V)
}
public interface Page<in V : Vision> {
public fun output(name: Name, meta: Meta = Meta.EMPTY): Output<V>?
}
public fun <V : Vision> Page<V>.render(name: Name, vision: V): Unit =
output(name)?.render(vision) ?: error("Could not resolve renderer for name $name")

View File

@ -3,7 +3,6 @@ package hep.dataforge.vision.html
import hep.dataforge.meta.DFExperimental import hep.dataforge.meta.DFExperimental
import hep.dataforge.meta.set import hep.dataforge.meta.set
import hep.dataforge.vision.VisionBase import hep.dataforge.vision.VisionBase
import hep.dataforge.vision.configure
import kotlinx.html.* import kotlinx.html.*
import kotlinx.html.stream.createHTML import kotlinx.html.stream.createHTML
import kotlin.test.Test import kotlin.test.Test
@ -54,7 +53,7 @@ class HtmlTagTest {
fun testStringRender() { fun testStringRender() {
println( println(
createHTML().div { createHTML().div {
renderVisionFragment<String>(simpleVisionRenderer, fragment = fragment) renderVisionFragment(simpleVisionRenderer, fragment = fragment)
} }
) )
} }

View File

@ -1,4 +1,4 @@
package hep.dataforge package hep.dataforge.vision
import kotlinx.browser.document import kotlinx.browser.document
import kotlinx.dom.hasClass import kotlinx.dom.hasClass

View File

@ -0,0 +1,22 @@
package hep.dataforge.vision
import hep.dataforge.context.Context
import hep.dataforge.context.Global
import hep.dataforge.vision.client.VisionClient
import hep.dataforge.vision.client.renderAllVisions
import kotlinx.browser.window
public actual val VisionForge: Context = Global.context("VisionForge").apply{
plugins.fetch(VisionManager)
plugins.fetch(VisionClient)
}
/**
* Render all visions in this [window] using current global state of [VisionForge]
*/
@JsExport
public fun renderVisionsInWindow(){
window.onload = {
VisionForge.plugins[VisionClient]?.renderAllVisions()
}
}

View File

@ -0,0 +1,8 @@
package hep.dataforge.vision
import hep.dataforge.context.Context
import hep.dataforge.context.Global
public actual val VisionForge: Context = Global.context("VisionForge").apply{
plugins.fetch(VisionManager)
}

View File

@ -1,8 +1,7 @@
package hep.dataforge.vision package hep.dataforge.vision.html
import hep.dataforge.context.Context
import hep.dataforge.meta.DFExperimental import hep.dataforge.meta.DFExperimental
import hep.dataforge.vision.html.HtmlFragment import hep.dataforge.vision.VisionManager
import kotlinx.html.link import kotlinx.html.link
import kotlinx.html.script import kotlinx.html.script
import kotlinx.html.unsafe import kotlinx.html.unsafe
@ -100,14 +99,14 @@ internal fun fileCssHeader(
* Make a script header, automatically copying file to appropriate location * Make a script header, automatically copying file to appropriate location
*/ */
@DFExperimental @DFExperimental
public fun Context.scriptHeader( public fun scriptHeader(
scriptResource: String, scriptResource: String,
htmlPath: Path, htmlPath: Path?,
resourceLocation: ResourceLocation, resourceLocation: ResourceLocation,
): HtmlFragment { ): HtmlFragment {
val targetPath = when (resourceLocation) { val targetPath = when (resourceLocation) {
ResourceLocation.LOCAL -> checkOrStoreFile( ResourceLocation.LOCAL -> checkOrStoreFile(
htmlPath, htmlPath ?: Path.of("."),
Path.of(VISIONFORGE_ASSETS_PATH), Path.of(VISIONFORGE_ASSETS_PATH),
scriptResource scriptResource
) )

View File

@ -0,0 +1,89 @@
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.HtmlVisionFragment
import hep.dataforge.vision.html.Page
import kotlinx.html.stream.createHTML
import java.awt.Desktop
import java.nio.file.Files
import java.nio.file.Path
//
///**
// * Create a full html string (including the head) for a given [HtmlVisionFragment]
// */
//@DFExperimental
//public fun Context.makeVisionString(
// fragment: HtmlVisionFragment,
// title: String = "VisionForge page",
// headerBuilder: () -> HtmlFragment,
//): String = createHTML().apply {
// head {
// meta {
// charset = "utf-8"
// fragment(headerBuilder())
// }
// title(title)
// }
// body {
// embedVisionFragment(visionManager, fragment = fragment)
// }
//}.finalize()
//
//
///**
// * Make a file with the embedded vision data
// */
//@DFExperimental
//public fun Context.makeVisionFile(
// fragment: HtmlVisionFragment,
// path: Path? = null,
// title: String = "VisionForge page",
// show: Boolean = true,
// headerBuilder: (Path) -> HtmlFragment,
//) {
// val actualFile = path?.let {
// Path.of(System.getProperty("user.home")).resolve(path)
// } ?: Files.createTempFile("tempPlot", ".html")
// //Files.createDirectories(actualFile.parent)
// val htmlString = makeVisionString(fragment, title) { headerBuilder(actualFile) }
//
// Files.writeString(actualFile, htmlString)
// if (show) {
// Desktop.getDesktop().browse(actualFile.toFile().toURI())
// }
//}
@DFExperimental
public fun Context.page(
title: String,
content: HtmlVisionFragment,
vararg headers: Pair<String, HtmlFragment>,
): Page = Page(this, title, mapOf(*headers), content)
@DFExperimental
public fun Page.makeFile(
path: Path?,
defaultHeaders: ((Path) -> Map<String,HtmlFragment>)? = null,
): Path {
val actualFile = path?.let {
Path.of(System.getProperty("user.home")).resolve(path)
} ?: Files.createTempFile("tempPlot", ".html")
val actualDefaultHeaders = defaultHeaders?.invoke(actualFile)
val actualPage = if(actualDefaultHeaders == null) this else copy(headers = actualDefaultHeaders + headers)
val htmlString = actualPage.render(createHTML())
Files.writeString(actualFile, htmlString)
return actualFile
}
@DFExperimental
public fun Page.show(path: Path? = null) {
val actualPath = makeFile(path)
Desktop.getDesktop().browse(actualPath.toFile().toURI())
}

View File

@ -1,51 +0,0 @@
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.HtmlVisionFragment
import hep.dataforge.vision.html.embedVisionFragment
import hep.dataforge.vision.html.fragment
import kotlinx.html.body
import kotlinx.html.head
import kotlinx.html.meta
import kotlinx.html.stream.createHTML
import kotlinx.html.title
import java.awt.Desktop
import java.nio.file.Files
import java.nio.file.Path
/**
* Make a file with the embedded vision data
*/
@DFExperimental
public fun Context.makeVisionFile(
fragment: HtmlVisionFragment,
path: Path? = null,
title: String = "VisionForge page",
show: Boolean = true,
headerBuilder: (Path) -> HtmlFragment,
) {
val actualFile = path?.let {
Path.of(System.getProperty("user.home")).resolve(path)
} ?: Files.createTempFile("tempPlot", ".html")
//Files.createDirectories(actualFile.parent)
val htmlString = createHTML().apply {
head {
meta {
charset = "utf-8"
fragment(headerBuilder(actualFile))
}
title(title)
}
body {
embedVisionFragment(visionManager, fragment = fragment)
}
}.finalize()
Files.writeString(actualFile, htmlString)
if (show) {
Desktop.getDesktop().browse(actualFile.toFile().toURI())
}
}

View File

@ -2,7 +2,6 @@ package hep.dataforge.vision.solid
import hep.dataforge.context.Context import hep.dataforge.context.Context
import hep.dataforge.context.ContextAware import hep.dataforge.context.ContextAware
import hep.dataforge.vision.layout.Output
import hep.dataforge.vision.solid.specifications.Canvas3DOptions import hep.dataforge.vision.solid.specifications.Canvas3DOptions
import javafx.application.Platform import javafx.application.Platform
import javafx.beans.property.ObjectProperty import javafx.beans.property.ObjectProperty
@ -12,8 +11,10 @@ import javafx.scene.paint.Color
import org.fxyz3d.scene.Axes import org.fxyz3d.scene.Axes
import tornadofx.* import tornadofx.*
class FXCanvas3D(val plugin: FX3DPlugin, val spec: Canvas3DOptions = Canvas3DOptions.empty()) : class FXCanvas3D(
Fragment(), Output<Solid>, ContextAware { val plugin: FX3DPlugin,
val spec: Canvas3DOptions = Canvas3DOptions.empty(),
) : Fragment(), ContextAware {
override val context: Context get() = plugin.context override val context: Context get() = plugin.context
@ -78,7 +79,7 @@ class FXCanvas3D(val plugin: FX3DPlugin, val spec: Canvas3DOptions = Canvas3DOpt
} }
} }
override fun render(vision: Solid) { fun render(vision: Solid) {
rootObject = vision rootObject = vision
} }
} }

View File

@ -9,6 +9,27 @@ kscience {
val plotlyVersion = "0.3.1-dev" val plotlyVersion = "0.3.1-dev"
kotlin { kotlin {
js{
//binaries.library()
binaries.executable()
browser {
webpackTask {
this.outputFileName = "js/visionforge-three.js"
}
}
}
afterEvaluate {
val jsBrowserDistribution by tasks.getting
tasks.getByName<ProcessResources>("jvmProcessResources") {
dependsOn(jsBrowserDistribution)
afterEvaluate {
from(jsBrowserDistribution)
}
}
}
sourceSets { sourceSets {
commonMain { commonMain {
dependencies { dependencies {

View File

@ -1,12 +1,16 @@
package hep.dataforge.vision.plotly package hep.dataforge.vision.plotly
import hep.dataforge.meta.Config
import hep.dataforge.meta.DFExperimental import hep.dataforge.meta.DFExperimental
import hep.dataforge.vision.VisionBase import hep.dataforge.vision.VisionBase
import hep.dataforge.vision.html.VisionOutput import hep.dataforge.vision.html.VisionOutput
import kscience.plotly.Plot import kscience.plotly.Plot
import kscience.plotly.Plotly import kscience.plotly.Plotly
public class VisionOfPlotly(public val plot: Plot): VisionBase(plot.config) @Serializable
public class VisionOfPlotly(private val plotConfig: Config) : VisionBase(plotConfig){
public val plot: Plot get() = Plot(plotConfig)
}
@DFExperimental @DFExperimental
public inline fun VisionOutput.plotly(block: Plot.() -> Unit): VisionOfPlotly = VisionOfPlotly(Plotly.plot(block)) public inline fun VisionOutput.plotly(block: Plot.() -> Unit): VisionOfPlotly = VisionOfPlotly(Plotly.plot(block).config)

View File

@ -0,0 +1,15 @@
package hep.dataforge.vision.plotly
import hep.dataforge.vision.Vision
import hep.dataforge.vision.VisionPlugin
import kotlinx.serialization.modules.SerializersModule
import kotlinx.serialization.modules.polymorphic
import kotlinx.serialization.modules.subclass
public expect class PlotlyPlugin : VisionPlugin
internal val plotlySerializersModule = SerializersModule {
polymorphic(Vision::class) {
subclass(VisionOfPlotly.serializer())
}
}

View File

@ -1,15 +0,0 @@
package hep.dataforge.vision.plotly
//public fun main() {
// val visionContext: Context = Global.context("vision-client")
//
// //Loading three-js renderer
// val threePlugin = visionContext.plugins.fetch(PlotlyPlugin)
//
// val clientManager = visionContext.plugins.fetch(VisionClient)
//
// //Fetch from server and render visions for all outputs
// window.onload = {
// clientManager.renderAllVisions()
// }
//}

View File

@ -1,23 +1,29 @@
package hep.dataforge.vision.plotly package hep.dataforge.vision.plotly
import hep.dataforge.context.AbstractPlugin import hep.dataforge.context.*
import hep.dataforge.context.Context
import hep.dataforge.context.PluginFactory
import hep.dataforge.context.PluginTag
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.vision.Vision import hep.dataforge.vision.Vision
import hep.dataforge.vision.VisionForge
import hep.dataforge.vision.VisionPlugin
import hep.dataforge.vision.client.ElementVisionRenderer import hep.dataforge.vision.client.ElementVisionRenderer
import hep.dataforge.vision.client.VisionClient
import kotlinx.serialization.modules.SerializersModule
import kscience.plotly.PlotlyConfig import kscience.plotly.PlotlyConfig
import kscience.plotly.plot import kscience.plotly.plot
import org.w3c.dom.Element import org.w3c.dom.Element
import kotlin.reflect.KClass import kotlin.reflect.KClass
public class PlotlyPlugin : AbstractPlugin(), ElementVisionRenderer { public actual class PlotlyPlugin : VisionPlugin(), ElementVisionRenderer {
public val visionClient: VisionClient by require(VisionClient)
override val tag: PluginTag get() = Companion.tag override val tag: PluginTag get() = Companion.tag
override fun rateVision(vision: Vision): Int = override val visionSerializersModule: SerializersModule get() = plotlySerializersModule
if (vision is VisionOfPlotly) ElementVisionRenderer.DEFAULT_RATING else ElementVisionRenderer.ZERO_RATING
override fun rateVision(vision: Vision): Int = when (vision) {
is VisionOfPlotly -> ElementVisionRenderer.DEFAULT_RATING
else -> ElementVisionRenderer.ZERO_RATING
}
override fun render(element: Element, vision: Vision, meta: Meta) { override fun render(element: Element, vision: Vision, meta: Meta) {
val plot = (vision as? VisionOfPlotly)?.plot ?: error("Only VisionOfPlotly visions are supported") val plot = (vision as? VisionOfPlotly)?.plot ?: error("Only VisionOfPlotly visions are supported")
@ -31,3 +37,11 @@ public class PlotlyPlugin : AbstractPlugin(), ElementVisionRenderer {
override fun invoke(meta: Meta, context: Context): PlotlyPlugin = PlotlyPlugin() override fun invoke(meta: Meta, context: Context): PlotlyPlugin = PlotlyPlugin()
} }
} }
/**
* Ensure that [PlotlyPlugin] is loaded in the global [VisionForge] context
*/
@JsExport
public fun withPlotly() {
VisionForge.plugins.fetch(PlotlyPlugin)
}

View File

@ -0,0 +1,30 @@
package hep.dataforge.vision.plotly
import hep.dataforge.meta.DFExperimental
import hep.dataforge.vision.html.HtmlFragment
import hep.dataforge.vision.html.ResourceLocation
import hep.dataforge.vision.html.scriptHeader
import kotlinx.html.script
import kotlinx.html.unsafe
import java.nio.file.Path
internal val plotlyScriptLocation = "js/visionforge-three.js"
/**
* A header that stores/embeds plotly bundle and registers plotly renderer in the frontend
*/
@OptIn(DFExperimental::class)
public fun plotlyHeader(location: ResourceLocation, filePath: Path? = null): HtmlFragment = {
scriptHeader(
plotlyScriptLocation,
filePath,
resourceLocation = location
).invoke(this)
script {
type = "text/javascript"
unsafe {
//language=JavaScript
+"hep.dataforge.vision.plotly.loadPlotly()"
}
}
}

View File

@ -0,0 +1,27 @@
package hep.dataforge.vision.plotly
import hep.dataforge.context.Context
import hep.dataforge.context.Plugin
import hep.dataforge.context.PluginFactory
import hep.dataforge.context.PluginTag
import hep.dataforge.meta.Meta
import hep.dataforge.vision.VisionPlugin
import kotlinx.serialization.modules.SerializersModule
import kotlin.reflect.KClass
public actual class PlotlyPlugin : VisionPlugin(), Plugin {
override val tag: PluginTag get() = Companion.tag
override val visionSerializersModule: SerializersModule get() = plotlySerializersModule
public companion object : PluginFactory<PlotlyPlugin> {
override val tag: PluginTag = PluginTag("vision.plotly", PluginTag.DATAFORGE_GROUP)
override val type: KClass<PlotlyPlugin> = PlotlyPlugin::class
override fun invoke(meta: Meta, context: Context): PlotlyPlugin = PlotlyPlugin()
}
}
public fun Context.withPlotly(): Context = apply {
plugins.fetch(PlotlyPlugin)
}

View File

@ -11,7 +11,6 @@ import hep.dataforge.values.ValueType
import hep.dataforge.values.asValue import hep.dataforge.values.asValue
import hep.dataforge.vision.* import hep.dataforge.vision.*
import hep.dataforge.vision.Vision.Companion.VISIBLE_KEY import hep.dataforge.vision.Vision.Companion.VISIBLE_KEY
import hep.dataforge.vision.layout.Output
import hep.dataforge.vision.solid.Solid.Companion.DETAIL_KEY import hep.dataforge.vision.solid.Solid.Companion.DETAIL_KEY
import hep.dataforge.vision.solid.Solid.Companion.IGNORE_KEY import hep.dataforge.vision.solid.Solid.Companion.IGNORE_KEY
import hep.dataforge.vision.solid.Solid.Companion.LAYER_KEY import hep.dataforge.vision.solid.Solid.Companion.LAYER_KEY
@ -108,9 +107,6 @@ public var Solid.layer: Int
setProperty(LAYER_KEY, value) setProperty(LAYER_KEY, value)
} }
@VisionBuilder
public fun Output<Solid>.solidGroup(builder: SolidGroup.() -> Unit): Unit = render(SolidGroup().apply(builder))
// Common properties // Common properties
public enum class RotationOrder { public enum class RotationOrder {

View File

@ -1,18 +1,11 @@
package hep.dataforge.vision.solid package hep.dataforge.vision.solid
import hep.dataforge.context.AbstractPlugin
import hep.dataforge.context.Context import hep.dataforge.context.Context
import hep.dataforge.context.PluginFactory import hep.dataforge.context.PluginFactory
import hep.dataforge.context.PluginTag import hep.dataforge.context.PluginTag
import hep.dataforge.meta.DFExperimental import hep.dataforge.meta.DFExperimental
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.names.Name import hep.dataforge.vision.*
import hep.dataforge.names.toName
import hep.dataforge.vision.Vision
import hep.dataforge.vision.VisionBase
import hep.dataforge.vision.VisionGroupBase
import hep.dataforge.vision.VisionManager
import hep.dataforge.vision.VisionManager.Companion.VISION_SERIALIZER_MODULE_TARGET
import hep.dataforge.vision.html.VisionOutput import hep.dataforge.vision.html.VisionOutput
import kotlinx.serialization.PolymorphicSerializer import kotlinx.serialization.PolymorphicSerializer
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
@ -23,16 +16,11 @@ import kotlinx.serialization.modules.subclass
import kotlin.reflect.KClass import kotlin.reflect.KClass
public class SolidManager(meta: Meta) : AbstractPlugin(meta) { public class SolidManager(meta: Meta) : VisionPlugin(meta) {
public val visionManager: VisionManager by require(VisionManager)
override val tag: PluginTag get() = Companion.tag override val tag: PluginTag get() = Companion.tag
override fun content(target: String): Map<Name, Any> = when (target) { override val visionSerializersModule: SerializersModule get() = serializersModuleForSolids
VISION_SERIALIZER_MODULE_TARGET -> mapOf(tag.name.toName() to serializersModuleForSolids)
else -> super.content(target)
}
public companion object : PluginFactory<SolidManager> { public companion object : PluginFactory<SolidManager> {
override val tag: PluginTag = PluginTag(name = "vision.solid", group = PluginTag.DATAFORGE_GROUP) override val tag: PluginTag = PluginTag(name = "vision.solid", group = PluginTag.DATAFORGE_GROUP)
@ -70,11 +58,14 @@ public class SolidManager(meta: Meta) : AbstractPlugin(meta) {
serializersModule = serializersModuleForSolids serializersModule = serializersModuleForSolids
} }
public fun encodeToString(solid: Solid): String = jsonForSolids.encodeToString(PolymorphicSerializer(Vision::class), solid) public fun encodeToString(solid: Solid): String =
jsonForSolids.encodeToString(PolymorphicSerializer(Vision::class), solid)
public fun decodeFromString(str: String): Solid = jsonForSolids.decodeFromString(PolymorphicSerializer(Solid::class), str) public fun decodeFromString(str: String): Solid =
jsonForSolids.decodeFromString(PolymorphicSerializer(Solid::class), str)
} }
} }
@VisionBuilder
@DFExperimental @DFExperimental
public inline fun VisionOutput.solid(block: SolidGroup.() -> Unit): SolidGroup = SolidGroup().apply(block) public inline fun VisionOutput.solid(block: SolidGroup.() -> Unit): SolidGroup = SolidGroup().apply(block)

View File

@ -4,16 +4,14 @@ import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaBuilder import hep.dataforge.meta.MetaBuilder
import hep.dataforge.meta.double import hep.dataforge.meta.double
import hep.dataforge.meta.get import hep.dataforge.meta.get
import kotlinx.serialization.Serializable
import kotlin.math.PI import kotlin.math.PI
public const val PI2: Float = 2 * PI.toFloat() public const val PI2: Float = 2 * PI.toFloat()
@Serializable @Serializable
public data class Point2D(public var x: Double, public var y: Double){ public data class Point2D(public var x: Double, public var y: Double)
public constructor(x: Number, y: Number) : this(x.toDouble(), y.toDouble())
}
public fun Point2D(x: Number, y: Number): Point2D = Point2D(x.toDouble(), y.toDouble())
public fun Point2D.toMeta(): Meta = Meta { public fun Point2D.toMeta(): Meta = Meta {
Solid.X_KEY put x Solid.X_KEY put x
@ -28,14 +26,14 @@ public data class Point3D(
public var y: Double, public var y: Double,
public var z: Double, public var z: Double,
) { ) {
public constructor(x: Number, y: Number, z: Number) : this(x.toDouble(), y.toDouble(), z.toDouble())
public companion object { public companion object {
public val ZERO: Point3D = Point3D(0.0, 0.0, 0.0) public val ZERO: Point3D = Point3D(0.0, 0.0, 0.0)
public val ONE: Point3D = Point3D(1.0, 1.0, 1.0) public val ONE: Point3D = Point3D(1.0, 1.0, 1.0)
} }
} }
public fun Point3D(x: Number, y: Number, z: Number): Point3D = Point3D(x.toDouble(), y.toDouble(), z.toDouble())
public operator fun Point3D.plus(other: Point3D): Point3D = Point3D( public operator fun Point3D.plus(other: Point3D): Point3D = Point3D(
this.x + other.x, this.x + other.x,
this.y + other.y, this.y + other.y,

View File

@ -6,6 +6,12 @@ kscience {
useSerialization() useSerialization()
} }
kotlin{
js{
binaries.library()
}
}
dependencies { dependencies {
api(project(":visionforge-solid")) api(project(":visionforge-solid"))
implementation(npm("three", "0.122.0")) implementation(npm("three", "0.122.0"))

View File

@ -1,12 +1,9 @@
package hep.dataforge.vision.solid.three package hep.dataforge.vision.solid.three
import hep.dataforge.meta.get
import hep.dataforge.meta.string
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.plus import hep.dataforge.names.plus
import hep.dataforge.names.toName import hep.dataforge.names.toName
import hep.dataforge.vision.Colors import hep.dataforge.vision.Colors
import hep.dataforge.vision.layout.Output
import hep.dataforge.vision.solid.Solid import hep.dataforge.vision.solid.Solid
import hep.dataforge.vision.solid.specifications.* import hep.dataforge.vision.solid.specifications.*
import hep.dataforge.vision.solid.three.ThreeMaterials.HIGHLIGHT_MATERIAL import hep.dataforge.vision.solid.three.ThreeMaterials.HIGHLIGHT_MATERIAL
@ -39,7 +36,7 @@ import kotlin.math.sin
public class ThreeCanvas( public class ThreeCanvas(
public val three: ThreePlugin, public val three: ThreePlugin,
public val options: Canvas3DOptions, public val options: Canvas3DOptions,
) : Output<Solid> { ) {
private var root: Object3D? = null private var root: Object3D? = null
private val raycaster = Raycaster() private val raycaster = Raycaster()
@ -195,7 +192,7 @@ public class ThreeCanvas(
} }
} }
public override fun render(vision: Solid) { public fun render(vision: Solid) {
scene.children.find { it.name == "@root" }?.let { scene.children.find { it.name == "@root" }?.let {
//Throw error is something is already rendered here //Throw error is something is already rendered here
error("Root object already is present in the canvas") error("Root object already is present in the canvas")

View File

@ -4,14 +4,13 @@ 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.VisionForge
import hep.dataforge.vision.client.ElementVisionRenderer import hep.dataforge.vision.client.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
import info.laht.threekt.core.Object3D import info.laht.threekt.core.Object3D
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import org.w3c.dom.Element import org.w3c.dom.Element
import org.w3c.dom.HTMLElement import org.w3c.dom.HTMLElement
import kotlin.collections.set import kotlin.collections.set
@ -151,6 +150,14 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
} }
} }
/**
* Ensure that [ThreePlugin] is loaded in the global [VisionForge] context
*/
@JsExport
public fun loadThreeJs(){
VisionForge.plugins.fetch(ThreePlugin)
}
public fun ThreePlugin.render( public fun ThreePlugin.render(
element: HTMLElement, element: HTMLElement,
obj: Solid, obj: Solid,

View File

@ -1,20 +1,9 @@
package hep.dataforge.vision.three.server package hep.dataforge.vision.three.server
import hep.dataforge.context.Global import hep.dataforge.vision.renderVisionsInWindow
import hep.dataforge.vision.client.VisionClient import hep.dataforge.vision.solid.three.loadThreeJs
import hep.dataforge.vision.client.renderAllVisions
import hep.dataforge.vision.solid.three.ThreePlugin
import kotlinx.browser.window
public fun main() { public fun main() {
//Loading three-js renderer loadThreeJs()
val visionContext = Global.context("threejs") { renderVisionsInWindow()
plugin(ThreePlugin)
}
val clientManager = visionContext.plugins.fetch(VisionClient)
//Fetch from server and render visions for all outputs
window.onload = {
clientManager.renderAllVisions()
}
} }

View File

@ -2,10 +2,12 @@ package hep.dataforge.vision.three.server
import hep.dataforge.context.Context import hep.dataforge.context.Context
import hep.dataforge.meta.DFExperimental import hep.dataforge.meta.DFExperimental
import hep.dataforge.vision.ResourceLocation
import hep.dataforge.vision.html.HtmlVisionFragment import hep.dataforge.vision.html.HtmlVisionFragment
import hep.dataforge.vision.makeVisionFile import hep.dataforge.vision.html.ResourceLocation
import hep.dataforge.vision.scriptHeader import hep.dataforge.vision.html.scriptHeader
import hep.dataforge.vision.makeFile
import hep.dataforge.vision.page
import java.awt.Desktop
import java.nio.file.Path import java.nio.file.Path
@ -14,12 +16,15 @@ public fun VisionServer.useThreeJs(): Unit {
} }
@DFExperimental @DFExperimental
public fun Context.makeVisionFile( public fun Context.makeThreeJsFile(
fragment: HtmlVisionFragment, content: HtmlVisionFragment,
path: Path? = null, path: Path? = null,
title: String = "VisionForge page", title: String = "VisionForge page",
resourceLocation: ResourceLocation = ResourceLocation.SYSTEM, resourceLocation: ResourceLocation = ResourceLocation.SYSTEM,
show: Boolean = true, show: Boolean = true,
): Unit = makeVisionFile(fragment, path = path, title = title, show = show) { actualPath -> ): Unit {
scriptHeader("js/visionforge-three.js", actualPath, resourceLocation) val actualPath = page(title, content).makeFile(path) { actualPath ->
mapOf("threeJs" to scriptHeader("js/visionforge-three.js", actualPath, resourceLocation))
}
if (show) Desktop.getDesktop().browse(actualPath.toFile().toURI())
} }