diff --git a/demo/gdml/src/jsMain/kotlin/hep/dataforge/vision/gdml/demo/GdmlJsDemoApp.kt b/demo/gdml/src/jsMain/kotlin/hep/dataforge/vision/gdml/demo/GdmlJsDemoApp.kt index 7aa28b22..f3aa4417 100644 --- a/demo/gdml/src/jsMain/kotlin/hep/dataforge/vision/gdml/demo/GdmlJsDemoApp.kt +++ b/demo/gdml/src/jsMain/kotlin/hep/dataforge/vision/gdml/demo/GdmlJsDemoApp.kt @@ -1,9 +1,9 @@ package hep.dataforge.vision.gdml.demo -import hep.dataforge.Application import hep.dataforge.context.Global -import hep.dataforge.startApplication +import hep.dataforge.vision.Application import hep.dataforge.vision.gdml.toVision +import hep.dataforge.vision.startApplication import kotlinx.browser.document import react.child import react.dom.render diff --git a/demo/muon-monitor/src/jsMain/kotlin/ru/mipt/npm/muon/monitor/MMDemoApp.kt b/demo/muon-monitor/src/jsMain/kotlin/ru/mipt/npm/muon/monitor/MMDemoApp.kt index 7ac877f5..9d66b314 100644 --- a/demo/muon-monitor/src/jsMain/kotlin/ru/mipt/npm/muon/monitor/MMDemoApp.kt +++ b/demo/muon-monitor/src/jsMain/kotlin/ru/mipt/npm/muon/monitor/MMDemoApp.kt @@ -1,8 +1,8 @@ package ru.mipt.npm.muon.monitor -import hep.dataforge.Application 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.features.json.JsonFeature import io.ktor.client.features.json.serializer.KotlinxSerializer diff --git a/demo/spatial-showcase/src/commonMain/kotlin/hep/dataforge/vision/solid/demo/demo.kt b/demo/spatial-showcase/src/commonMain/kotlin/hep/dataforge/vision/solid/demo/demo.kt index 2dacf455..c5d3db45 100644 --- a/demo/spatial-showcase/src/commonMain/kotlin/hep/dataforge/vision/solid/demo/demo.kt +++ b/demo/spatial-showcase/src/commonMain/kotlin/hep/dataforge/vision/solid/demo/demo.kt @@ -4,7 +4,7 @@ import hep.dataforge.meta.Meta import hep.dataforge.meta.invoke import hep.dataforge.names.toName 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.specifications.Canvas3DOptions import hep.dataforge.vision.visible @@ -15,12 +15,12 @@ import kotlin.math.sin import kotlin.random.Random -fun Page.demo(name: String, title: String = name, block: SolidGroup.() -> Unit) { +fun VisionLayout.demo(name: String, title: String = name, block: SolidGroup.() -> Unit) { val meta = Meta { "title" put title } - val output = output(name.toName(), meta)?: error("Output with name $name not found") - output.solidGroup (builder = block) + val vision = SolidGroup(block) + render(name.toName(), vision) } val canvasOptions = Canvas3DOptions { @@ -35,7 +35,7 @@ val canvasOptions = Canvas3DOptions { } } -fun Page.showcase() { +fun VisionLayout.showcase() { demo("shapes", "Basic shapes") { box(100.0, 100.0, 100.0) { z = -110.0 @@ -136,14 +136,14 @@ fun Page.showcase() { } } -fun Page.showcaseCSG() { +fun VisionLayout.showcaseCSG() { demo("CSG.simple", "CSG operations") { composite(CompositeType.INTERSECT) { y = 300 box(100, 100, 100) { z = 50 } - sphere(50){ + sphere(50) { detail = 32 } material { @@ -154,7 +154,7 @@ fun Page.showcaseCSG() { box(100, 100, 100) { z = 50 } - sphere(50){ + sphere(50) { detail = 32 } color("lightgreen") @@ -165,7 +165,7 @@ fun Page.showcaseCSG() { box(100, 100, 100) { z = 50 } - sphere(50){ + sphere(50) { detail = 32 } color("teal") diff --git a/demo/spatial-showcase/src/jsMain/kotlin/hep/dataforge/vision/solid/demo/ThreeDemoApp.kt b/demo/spatial-showcase/src/jsMain/kotlin/hep/dataforge/vision/solid/demo/ThreeDemoApp.kt index 2e613622..8a2bcdd0 100644 --- a/demo/spatial-showcase/src/jsMain/kotlin/hep/dataforge/vision/solid/demo/ThreeDemoApp.kt +++ b/demo/spatial-showcase/src/jsMain/kotlin/hep/dataforge/vision/solid/demo/ThreeDemoApp.kt @@ -1,9 +1,9 @@ package hep.dataforge.vision.solid.demo -import hep.dataforge.Application -import hep.dataforge.startApplication +import hep.dataforge.vision.Application import hep.dataforge.vision.solid.x import hep.dataforge.vision.solid.y +import hep.dataforge.vision.startApplication import kotlinx.browser.document import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.delay diff --git a/demo/spatial-showcase/src/jsMain/kotlin/hep/dataforge/vision/solid/demo/ThreeDemoGrid.kt b/demo/spatial-showcase/src/jsMain/kotlin/hep/dataforge/vision/solid/demo/ThreeDemoGrid.kt index f7268a12..92d63d16 100644 --- a/demo/spatial-showcase/src/jsMain/kotlin/hep/dataforge/vision/solid/demo/ThreeDemoGrid.kt +++ b/demo/spatial-showcase/src/jsMain/kotlin/hep/dataforge/vision/solid/demo/ThreeDemoGrid.kt @@ -5,8 +5,7 @@ import hep.dataforge.meta.Meta import hep.dataforge.meta.get import hep.dataforge.meta.string import hep.dataforge.names.Name -import hep.dataforge.vision.layout.Output -import hep.dataforge.vision.layout.Page +import hep.dataforge.vision.VisionLayout import hep.dataforge.vision.solid.Solid import hep.dataforge.vision.solid.three.ThreeCanvas 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.HTMLElement -class ThreeDemoGrid(element: Element) : Page { +class ThreeDemoGrid(element: Element) : VisionLayout { private lateinit var navigationElement: HTMLElement private lateinit var contentElement: HTMLDivElement @@ -47,31 +46,31 @@ class ThreeDemoGrid(element: Element) : Page { } } - - @Suppress("UNCHECKED_CAST") - override fun output(name: Name, meta: Meta): Output = outputs.getOrPut(name) { - navigationElement.append { - li("nav-item") { - a(classes = "nav-link") { - href = "#$name" - +name.toString() + override fun render(name: Name, vision: Solid, meta: Meta) { + outputs.getOrPut(name) { + navigationElement.append { + li("nav-item") { + a(classes = "nav-link") { + href = "#$name" + +name.toString() + } } } - } - contentElement.append { - div("container") { - id = name.toString() - hr() - h2 { +(meta["title"].string ?: name.toString()) } - hr() - div { - style = "height: 600px;" - id = "output-$name" + contentElement.append { + div("container") { + id = name.toString() + hr() + h2 { +(meta["title"].string ?: name.toString()) } + hr() + div { + style = "height: 600px;" + id = "output-$name" + } } } - } - val element = document.getElementById("output-$name") ?: error("Element not found") - three.createCanvas(element, canvasOptions) + val element = document.getElementById("output-$name") ?: error("Element not found") + three.createCanvas(element, canvasOptions) + }.render(vision) } } diff --git a/demo/spatial-showcase/src/jvmMain/kotlin/hep/dataforge/vision/solid/demo/FXDemoGrid.kt b/demo/spatial-showcase/src/jvmMain/kotlin/hep/dataforge/vision/solid/demo/FXDemoGrid.kt index 6b5267ff..62b9cc20 100644 --- a/demo/spatial-showcase/src/jvmMain/kotlin/hep/dataforge/vision/solid/demo/FXDemoGrid.kt +++ b/demo/spatial-showcase/src/jvmMain/kotlin/hep/dataforge/vision/solid/demo/FXDemoGrid.kt @@ -3,8 +3,7 @@ package hep.dataforge.vision.solid.demo import hep.dataforge.context.Global import hep.dataforge.meta.Meta import hep.dataforge.names.Name -import hep.dataforge.vision.layout.Output -import hep.dataforge.vision.layout.Page +import hep.dataforge.vision.VisionLayout import hep.dataforge.vision.solid.FX3DPlugin import hep.dataforge.vision.solid.FXCanvas3D import hep.dataforge.vision.solid.Solid @@ -13,7 +12,7 @@ import javafx.scene.Parent import javafx.scene.control.Tab import tornadofx.* -class FXDemoGrid : View(title = "DataForge-vis FX demo"), Page { +class FXDemoGrid : View(title = "DataForge-vis FX demo"), VisionLayout { private val outputs = FXCollections.observableHashMap() override val root: Parent = borderpane { @@ -26,8 +25,8 @@ class FXDemoGrid : View(title = "DataForge-vis FX demo"), Page { private val fx3d = Global.plugins.fetch(FX3DPlugin) - override fun output(name: Name, meta: Meta): Output = outputs.getOrPut(name) { - FXCanvas3D(fx3d, canvasOptions) + override fun render(name: Name, vision: Solid, meta: Meta) { + outputs.getOrPut(name) { FXCanvas3D(fx3d, canvasOptions) }.render(vision) } } \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 4d9ca164..da9702f9 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME 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 zipStorePath=wrapper/dists diff --git a/playground/src/commonMain/kotlin/visionContext.kt b/playground/src/commonMain/kotlin/visionContext.kt deleted file mode 100644 index 139597f9..00000000 --- a/playground/src/commonMain/kotlin/visionContext.kt +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/playground/src/jsMain/kotlin/playgroundMain.kt b/playground/src/jsMain/kotlin/playgroundMain.kt index 6c577e6f..a2a4bd90 100644 --- a/playground/src/jsMain/kotlin/playgroundMain.kt +++ b/playground/src/jsMain/kotlin/playgroundMain.kt @@ -1,27 +1,9 @@ -import hep.dataforge.context.Context -import hep.dataforge.context.Global -import hep.dataforge.meta.DFExperimental -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 +import hep.dataforge.vision.plotly.withPlotly +import hep.dataforge.vision.renderVisionsInWindow +import hep.dataforge.vision.solid.three.loadThreeJs -@DFExperimental fun main() { - - val visionContext: Context = Global.context("VISION") { - plugin(ThreePlugin) - 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) + withPlotly() + loadThreeJs() + renderVisionsInWindow() } \ No newline at end of file diff --git a/playground/src/jvmMain/kotlin/hep/dataforge/vision/solid/generateSchema.kt b/playground/src/jvmMain/kotlin/generateSchema.kt similarity index 98% rename from playground/src/jvmMain/kotlin/hep/dataforge/vision/solid/generateSchema.kt rename to playground/src/jvmMain/kotlin/generateSchema.kt index a7ccea62..e3411516 100644 --- a/playground/src/jvmMain/kotlin/hep/dataforge/vision/solid/generateSchema.kt +++ b/playground/src/jvmMain/kotlin/generateSchema.kt @@ -1,6 +1,8 @@ -package hep.dataforge.vision.solid +package hep.dataforge.vision.examples import com.github.ricky12awesome.jss.encodeToSchema +import hep.dataforge.vision.solid.SolidGroup +import hep.dataforge.vision.solid.SolidManager import kotlinx.serialization.json.Json fun main() { diff --git a/playground/src/jvmMain/kotlin/hep/dataforge/vision/solid/serverExtensions.kt b/playground/src/jvmMain/kotlin/hep/dataforge/vision/solid/serverExtensions.kt deleted file mode 100644 index 2e365104..00000000 --- a/playground/src/jvmMain/kotlin/hep/dataforge/vision/solid/serverExtensions.kt +++ /dev/null @@ -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) -} diff --git a/playground/src/jvmMain/kotlin/hep/dataforge/vision/solid/simpleCube.kt b/playground/src/jvmMain/kotlin/hep/dataforge/vision/solid/simpleCube.kt deleted file mode 100644 index c1d20ef7..00000000 --- a/playground/src/jvmMain/kotlin/hep/dataforge/vision/solid/simpleCube.kt +++ /dev/null @@ -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) -} \ No newline at end of file diff --git a/playground/src/jvmMain/kotlin/plotlyVision.kt b/playground/src/jvmMain/kotlin/plotlyVision.kt new file mode 100644 index 00000000..ea4be39b --- /dev/null +++ b/playground/src/jvmMain/kotlin/plotlyVision.kt @@ -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) +} \ No newline at end of file diff --git a/playground/src/jvmMain/kotlin/hep/dataforge/vision/solid/randomSpheres.kt b/playground/src/jvmMain/kotlin/randomSpheres.kt similarity index 86% rename from playground/src/jvmMain/kotlin/hep/dataforge/vision/solid/randomSpheres.kt rename to playground/src/jvmMain/kotlin/randomSpheres.kt index 90031fd3..4d3a773b 100644 --- a/playground/src/jvmMain/kotlin/hep/dataforge/vision/solid/randomSpheres.kt +++ b/playground/src/jvmMain/kotlin/randomSpheres.kt @@ -1,9 +1,11 @@ -package hep.dataforge.vision.solid +package hep.dataforge.vision.examples import hep.dataforge.meta.DFExperimental -import hep.dataforge.vision.ResourceLocation +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.* import kotlinx.html.h1 import java.nio.file.Paths import kotlin.random.Random diff --git a/playground/src/jvmMain/kotlin/serverExtensions.kt b/playground/src/jvmMain/kotlin/serverExtensions.kt new file mode 100644 index 00000000..51aa23af --- /dev/null +++ b/playground/src/jvmMain/kotlin/serverExtensions.kt @@ -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) +//} diff --git a/playground/src/jvmMain/kotlin/simpleCube.kt b/playground/src/jvmMain/kotlin/simpleCube.kt new file mode 100644 index 00000000..b7ac54b9 --- /dev/null +++ b/playground/src/jvmMain/kotlin/simpleCube.kt @@ -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) +} \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionForge.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionForge.kt new file mode 100644 index 00000000..2058c3cf --- /dev/null +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionForge.kt @@ -0,0 +1,5 @@ +package hep.dataforge.vision + +import hep.dataforge.context.Context + +public expect val VisionForge: Context \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionLayout.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionLayout.kt new file mode 100644 index 00000000..ae86a0c8 --- /dev/null +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionLayout.kt @@ -0,0 +1,8 @@ +package hep.dataforge.vision + +import hep.dataforge.meta.Meta +import hep.dataforge.names.Name + +public interface VisionLayout { + public fun render(name: Name, vision: V, meta: Meta = Meta.EMPTY) +} \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionManager.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionManager.kt index 9a831799..ea106cab 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionManager.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionManager.kt @@ -3,6 +3,8 @@ package hep.dataforge.vision import hep.dataforge.context.* import hep.dataforge.meta.* import hep.dataforge.meta.descriptors.NodeDescriptor +import hep.dataforge.names.Name +import hep.dataforge.names.toName import kotlinx.serialization.PolymorphicSerializer import kotlinx.serialization.json.Json 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 = when (target) { + VisionManager.VISION_SERIALIZER_MODULE_TARGET -> mapOf(tag.toString().toName() to visionSerializersModule) + else -> super.content(target) + } + +} + /** * Fetch a [VisionManager] from this plugin */ diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/html/Page.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/html/Page.kt new file mode 100644 index 00000000..942d5ff5 --- /dev/null +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/html/Page.kt @@ -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, + public val content: HtmlVisionFragment +) { + public fun render(root: TagConsumer): R = root.apply { + head { + meta { + charset = "utf-8" + headers.values.forEach { + fragment(it) + } + } + title(title) + } + body { + embedVisionFragment(context.visionManager, fragment = content) + } + }.finalize() +} \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/html/staticHtmlRender.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/html/staticHtmlRender.kt index 5a275e82..ebe63603 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/html/staticHtmlRender.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/html/staticHtmlRender.kt @@ -4,19 +4,18 @@ import hep.dataforge.meta.Meta import hep.dataforge.names.Name import hep.dataforge.vision.Vision import hep.dataforge.vision.VisionManager -import kotlinx.html.DIV -import kotlinx.html.FlowContent -import kotlinx.html.script -import kotlinx.html.unsafe +import kotlinx.html.* -public fun FlowContent.embedVisionFragment( +public fun TagConsumer<*>.embedVisionFragment( manager: VisionManager, idPrefix: String? = null, fragment: HtmlVisionFragment, -) { - val consumer = object : VisionTagConsumer(consumer, idPrefix) { +): Map { + val visionMap = HashMap() + val consumer = object : VisionTagConsumer(this@embedVisionFragment, idPrefix) { override fun DIV.renderVision(name: Name, vision: Vision, outputMeta: Meta) { + visionMap[name] = vision script { type = "text/json" attributes["class"] = OUTPUT_DATA_CLASS @@ -27,17 +26,29 @@ public fun FlowContent.embedVisionFragment( } } fragment(consumer) + return visionMap } +public fun FlowContent.embedVisionFragment( + manager: VisionManager, + idPrefix: String? = null, + fragment: HtmlVisionFragment, +): Map = consumer.embedVisionFragment(manager, idPrefix, fragment) + public typealias HtmlVisionRenderer = FlowContent.(name: Name, vision: Vision, meta: Meta) -> Unit -public fun FlowContent.renderVisionFragment( +public fun FlowContent.renderVisionFragment( renderer: DIV.(name: Name, vision: Vision, meta: Meta) -> Unit, idPrefix: String? = null, fragment: HtmlVisionFragment, -) { +): Map { + val visionMap = HashMap() val consumer = object : VisionTagConsumer(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) + return visionMap } \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/layout/Page.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/layout/Page.kt deleted file mode 100644 index 848e0dc7..00000000 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/layout/Page.kt +++ /dev/null @@ -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 { - public fun render(vision: V) -} - -public interface Page { - public fun output(name: Name, meta: Meta = Meta.EMPTY): Output? -} - -public fun Page.render(name: Name, vision: V): Unit = - output(name)?.render(vision) ?: error("Could not resolve renderer for name $name") \ No newline at end of file diff --git a/visionforge-core/src/commonTest/kotlin/hep/dataforge/vision/html/HtmlTagTest.kt b/visionforge-core/src/commonTest/kotlin/hep/dataforge/vision/html/HtmlTagTest.kt index 2d2ad125..22008800 100644 --- a/visionforge-core/src/commonTest/kotlin/hep/dataforge/vision/html/HtmlTagTest.kt +++ b/visionforge-core/src/commonTest/kotlin/hep/dataforge/vision/html/HtmlTagTest.kt @@ -3,7 +3,6 @@ package hep.dataforge.vision.html import hep.dataforge.meta.DFExperimental import hep.dataforge.meta.set import hep.dataforge.vision.VisionBase -import hep.dataforge.vision.configure import kotlinx.html.* import kotlinx.html.stream.createHTML import kotlin.test.Test @@ -54,7 +53,7 @@ class HtmlTagTest { fun testStringRender() { println( createHTML().div { - renderVisionFragment(simpleVisionRenderer, fragment = fragment) + renderVisionFragment(simpleVisionRenderer, fragment = fragment) } ) } diff --git a/visionforge-core/src/jsMain/kotlin/hep/dataforge/Application.kt b/visionforge-core/src/jsMain/kotlin/hep/dataforge/vision/Application.kt similarity index 98% rename from visionforge-core/src/jsMain/kotlin/hep/dataforge/Application.kt rename to visionforge-core/src/jsMain/kotlin/hep/dataforge/vision/Application.kt index fb182522..78f6acc8 100644 --- a/visionforge-core/src/jsMain/kotlin/hep/dataforge/Application.kt +++ b/visionforge-core/src/jsMain/kotlin/hep/dataforge/vision/Application.kt @@ -1,4 +1,4 @@ -package hep.dataforge +package hep.dataforge.vision import kotlinx.browser.document import kotlinx.dom.hasClass diff --git a/visionforge-core/src/jsMain/kotlin/hep/dataforge/vision/VisionForge.kt b/visionforge-core/src/jsMain/kotlin/hep/dataforge/vision/VisionForge.kt new file mode 100644 index 00000000..6f6ca28a --- /dev/null +++ b/visionforge-core/src/jsMain/kotlin/hep/dataforge/vision/VisionForge.kt @@ -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() + } +} \ No newline at end of file diff --git a/visionforge-core/src/jvmMain/kotlin/hep/dataforge/vision/VisionForge.kt b/visionforge-core/src/jvmMain/kotlin/hep/dataforge/vision/VisionForge.kt new file mode 100644 index 00000000..67b656b8 --- /dev/null +++ b/visionforge-core/src/jvmMain/kotlin/hep/dataforge/vision/VisionForge.kt @@ -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) +} \ No newline at end of file diff --git a/visionforge-core/src/jvmMain/kotlin/hep/dataforge/vision/headers.kt b/visionforge-core/src/jvmMain/kotlin/hep/dataforge/vision/html/headers.kt similarity index 95% rename from visionforge-core/src/jvmMain/kotlin/hep/dataforge/vision/headers.kt rename to visionforge-core/src/jvmMain/kotlin/hep/dataforge/vision/html/headers.kt index 5dfb5352..4c458b58 100644 --- a/visionforge-core/src/jvmMain/kotlin/hep/dataforge/vision/headers.kt +++ b/visionforge-core/src/jvmMain/kotlin/hep/dataforge/vision/html/headers.kt @@ -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.vision.html.HtmlFragment +import hep.dataforge.vision.VisionManager import kotlinx.html.link import kotlinx.html.script import kotlinx.html.unsafe @@ -100,14 +99,14 @@ internal fun fileCssHeader( * Make a script header, automatically copying file to appropriate location */ @DFExperimental -public fun Context.scriptHeader( +public fun scriptHeader( scriptResource: String, - htmlPath: Path, + htmlPath: Path?, resourceLocation: ResourceLocation, ): HtmlFragment { val targetPath = when (resourceLocation) { ResourceLocation.LOCAL -> checkOrStoreFile( - htmlPath, + htmlPath ?: Path.of("."), Path.of(VISIONFORGE_ASSETS_PATH), scriptResource ) diff --git a/visionforge-core/src/jvmMain/kotlin/hep/dataforge/vision/html/htmlExport.kt b/visionforge-core/src/jvmMain/kotlin/hep/dataforge/vision/html/htmlExport.kt new file mode 100644 index 00000000..337ecefc --- /dev/null +++ b/visionforge-core/src/jvmMain/kotlin/hep/dataforge/vision/html/htmlExport.kt @@ -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, +): Page = Page(this, title, mapOf(*headers), content) + + +@DFExperimental +public fun Page.makeFile( + path: Path?, + defaultHeaders: ((Path) -> Map)? = 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()) +} \ No newline at end of file diff --git a/visionforge-core/src/jvmMain/kotlin/hep/dataforge/vision/htmlExport.kt b/visionforge-core/src/jvmMain/kotlin/hep/dataforge/vision/htmlExport.kt deleted file mode 100644 index d560f46a..00000000 --- a/visionforge-core/src/jvmMain/kotlin/hep/dataforge/vision/htmlExport.kt +++ /dev/null @@ -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()) - } -} \ No newline at end of file diff --git a/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/FXCanvas3D.kt b/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/FXCanvas3D.kt index 69f484fd..8cef5208 100644 --- a/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/FXCanvas3D.kt +++ b/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/FXCanvas3D.kt @@ -2,7 +2,6 @@ package hep.dataforge.vision.solid import hep.dataforge.context.Context import hep.dataforge.context.ContextAware -import hep.dataforge.vision.layout.Output import hep.dataforge.vision.solid.specifications.Canvas3DOptions import javafx.application.Platform import javafx.beans.property.ObjectProperty @@ -12,8 +11,10 @@ import javafx.scene.paint.Color import org.fxyz3d.scene.Axes import tornadofx.* -class FXCanvas3D(val plugin: FX3DPlugin, val spec: Canvas3DOptions = Canvas3DOptions.empty()) : - Fragment(), Output, ContextAware { +class FXCanvas3D( + val plugin: FX3DPlugin, + val spec: Canvas3DOptions = Canvas3DOptions.empty(), +) : Fragment(), ContextAware { 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 } } \ No newline at end of file diff --git a/visionforge-plotly/build.gradle.kts b/visionforge-plotly/build.gradle.kts index 51419b1e..66c1ca12 100644 --- a/visionforge-plotly/build.gradle.kts +++ b/visionforge-plotly/build.gradle.kts @@ -9,6 +9,27 @@ kscience { val plotlyVersion = "0.3.1-dev" kotlin { + js{ + //binaries.library() + binaries.executable() + browser { + webpackTask { + this.outputFileName = "js/visionforge-three.js" + } + } + } + + afterEvaluate { + val jsBrowserDistribution by tasks.getting + + tasks.getByName("jvmProcessResources") { + dependsOn(jsBrowserDistribution) + afterEvaluate { + from(jsBrowserDistribution) + } + } + } + sourceSets { commonMain { dependencies { diff --git a/visionforge-plotly/src/commonMain/kotlin/hep/dataforge/vision/plotly/VisionOfPlotly.kt b/visionforge-plotly/src/commonMain/kotlin/hep/dataforge/vision/plotly/VisionOfPlotly.kt index 997a560a..4e81cd12 100644 --- a/visionforge-plotly/src/commonMain/kotlin/hep/dataforge/vision/plotly/VisionOfPlotly.kt +++ b/visionforge-plotly/src/commonMain/kotlin/hep/dataforge/vision/plotly/VisionOfPlotly.kt @@ -1,12 +1,16 @@ package hep.dataforge.vision.plotly +import hep.dataforge.meta.Config import hep.dataforge.meta.DFExperimental import hep.dataforge.vision.VisionBase import hep.dataforge.vision.html.VisionOutput import kscience.plotly.Plot 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 -public inline fun VisionOutput.plotly(block: Plot.() -> Unit): VisionOfPlotly = VisionOfPlotly(Plotly.plot(block)) \ No newline at end of file +public inline fun VisionOutput.plotly(block: Plot.() -> Unit): VisionOfPlotly = VisionOfPlotly(Plotly.plot(block).config) \ No newline at end of file diff --git a/visionforge-plotly/src/commonMain/kotlin/hep/dataforge/vision/plotly/plotlyCommon.kt b/visionforge-plotly/src/commonMain/kotlin/hep/dataforge/vision/plotly/plotlyCommon.kt new file mode 100644 index 00000000..f7a95f1f --- /dev/null +++ b/visionforge-plotly/src/commonMain/kotlin/hep/dataforge/vision/plotly/plotlyCommon.kt @@ -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()) + } +} \ No newline at end of file diff --git a/visionforge-plotly/src/jsMain/kotlin/hep/dataforge/vision/plotly/jsMain.kt b/visionforge-plotly/src/jsMain/kotlin/hep/dataforge/vision/plotly/jsMain.kt deleted file mode 100644 index ae0ffb37..00000000 --- a/visionforge-plotly/src/jsMain/kotlin/hep/dataforge/vision/plotly/jsMain.kt +++ /dev/null @@ -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() -// } -//} \ No newline at end of file diff --git a/visionforge-plotly/src/jsMain/kotlin/hep/dataforge/vision/plotly/PlotlyPlugin.kt b/visionforge-plotly/src/jsMain/kotlin/hep/dataforge/vision/plotly/plotlyJs.kt similarity index 53% rename from visionforge-plotly/src/jsMain/kotlin/hep/dataforge/vision/plotly/PlotlyPlugin.kt rename to visionforge-plotly/src/jsMain/kotlin/hep/dataforge/vision/plotly/plotlyJs.kt index 9e2793dc..097c7a54 100644 --- a/visionforge-plotly/src/jsMain/kotlin/hep/dataforge/vision/plotly/PlotlyPlugin.kt +++ b/visionforge-plotly/src/jsMain/kotlin/hep/dataforge/vision/plotly/plotlyJs.kt @@ -1,23 +1,29 @@ package hep.dataforge.vision.plotly -import hep.dataforge.context.AbstractPlugin -import hep.dataforge.context.Context -import hep.dataforge.context.PluginFactory -import hep.dataforge.context.PluginTag +import hep.dataforge.context.* import hep.dataforge.meta.Meta 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.VisionClient +import kotlinx.serialization.modules.SerializersModule import kscience.plotly.PlotlyConfig import kscience.plotly.plot import org.w3c.dom.Element 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 fun rateVision(vision: Vision): Int = - if (vision is VisionOfPlotly) ElementVisionRenderer.DEFAULT_RATING else ElementVisionRenderer.ZERO_RATING + override val visionSerializersModule: SerializersModule get() = plotlySerializersModule + + 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) { val plot = (vision as? VisionOfPlotly)?.plot ?: error("Only VisionOfPlotly visions are supported") @@ -30,4 +36,12 @@ public class PlotlyPlugin : AbstractPlugin(), ElementVisionRenderer { override val type: KClass = PlotlyPlugin::class 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) } \ No newline at end of file diff --git a/visionforge-plotly/src/jvmMain/kotlin/hep/dataforge/vision/plotly/plotlyHeaders.kt b/visionforge-plotly/src/jvmMain/kotlin/hep/dataforge/vision/plotly/plotlyHeaders.kt new file mode 100644 index 00000000..ecf32a1a --- /dev/null +++ b/visionforge-plotly/src/jvmMain/kotlin/hep/dataforge/vision/plotly/plotlyHeaders.kt @@ -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()" + } + } +} diff --git a/visionforge-plotly/src/jvmMain/kotlin/hep/dataforge/vision/plotly/plotlyJvm.kt b/visionforge-plotly/src/jvmMain/kotlin/hep/dataforge/vision/plotly/plotlyJvm.kt new file mode 100644 index 00000000..e0a2ec5b --- /dev/null +++ b/visionforge-plotly/src/jvmMain/kotlin/hep/dataforge/vision/plotly/plotlyJvm.kt @@ -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 { + override val tag: PluginTag = PluginTag("vision.plotly", PluginTag.DATAFORGE_GROUP) + override val type: KClass = PlotlyPlugin::class + override fun invoke(meta: Meta, context: Context): PlotlyPlugin = PlotlyPlugin() + } +} + +public fun Context.withPlotly(): Context = apply { + plugins.fetch(PlotlyPlugin) +} \ No newline at end of file diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Solid.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Solid.kt index 1050dff1..96e4f33a 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Solid.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Solid.kt @@ -11,7 +11,6 @@ import hep.dataforge.values.ValueType import hep.dataforge.values.asValue import hep.dataforge.vision.* 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.IGNORE_KEY import hep.dataforge.vision.solid.Solid.Companion.LAYER_KEY @@ -108,9 +107,6 @@ public var Solid.layer: Int setProperty(LAYER_KEY, value) } -@VisionBuilder -public fun Output.solidGroup(builder: SolidGroup.() -> Unit): Unit = render(SolidGroup().apply(builder)) - // Common properties public enum class RotationOrder { diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidManager.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidManager.kt index f55dc1f4..c6d610cb 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidManager.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidManager.kt @@ -1,18 +1,11 @@ package hep.dataforge.vision.solid -import hep.dataforge.context.AbstractPlugin import hep.dataforge.context.Context import hep.dataforge.context.PluginFactory import hep.dataforge.context.PluginTag import hep.dataforge.meta.DFExperimental import hep.dataforge.meta.Meta -import hep.dataforge.names.Name -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.* import hep.dataforge.vision.html.VisionOutput import kotlinx.serialization.PolymorphicSerializer import kotlinx.serialization.json.Json @@ -23,16 +16,11 @@ import kotlinx.serialization.modules.subclass import kotlin.reflect.KClass -public class SolidManager(meta: Meta) : AbstractPlugin(meta) { - - public val visionManager: VisionManager by require(VisionManager) +public class SolidManager(meta: Meta) : VisionPlugin(meta) { override val tag: PluginTag get() = Companion.tag - override fun content(target: String): Map = when (target) { - VISION_SERIALIZER_MODULE_TARGET -> mapOf(tag.name.toName() to serializersModuleForSolids) - else -> super.content(target) - } + override val visionSerializersModule: SerializersModule get() = serializersModuleForSolids public companion object : PluginFactory { override val tag: PluginTag = PluginTag(name = "vision.solid", group = PluginTag.DATAFORGE_GROUP) @@ -66,15 +54,18 @@ public class SolidManager(meta: Meta) : AbstractPlugin(meta) { } } - internal val jsonForSolids: Json = Json(VisionManager.defaultJson){ + internal val jsonForSolids: Json = Json(VisionManager.defaultJson) { 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 public inline fun VisionOutput.solid(block: SolidGroup.() -> Unit): SolidGroup = SolidGroup().apply(block) \ No newline at end of file diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/geometry.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/geometry.kt index 94c61da5..ae28ca53 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/geometry.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/geometry.kt @@ -4,16 +4,14 @@ import hep.dataforge.meta.Meta import hep.dataforge.meta.MetaBuilder import hep.dataforge.meta.double import hep.dataforge.meta.get -import kotlinx.serialization.Serializable import kotlin.math.PI public const val PI2: Float = 2 * PI.toFloat() @Serializable -public data class Point2D(public var x: Double, public var y: Double){ - public constructor(x: Number, y: Number) : this(x.toDouble(), y.toDouble()) -} +public data class Point2D(public var x: Double, public var y: Double) +public fun Point2D(x: Number, y: Number): Point2D = Point2D(x.toDouble(), y.toDouble()) public fun Point2D.toMeta(): Meta = Meta { Solid.X_KEY put x @@ -28,14 +26,14 @@ public data class Point3D( public var y: 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 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( this.x + other.x, this.y + other.y, diff --git a/visionforge-threejs/build.gradle.kts b/visionforge-threejs/build.gradle.kts index 4075c8df..d692c548 100644 --- a/visionforge-threejs/build.gradle.kts +++ b/visionforge-threejs/build.gradle.kts @@ -6,6 +6,12 @@ kscience { useSerialization() } +kotlin{ + js{ + binaries.library() + } +} + dependencies { api(project(":visionforge-solid")) implementation(npm("three", "0.122.0")) diff --git a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeCanvas.kt b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeCanvas.kt index 6df44691..2a91b8cc 100644 --- a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeCanvas.kt +++ b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeCanvas.kt @@ -1,12 +1,9 @@ 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.plus import hep.dataforge.names.toName import hep.dataforge.vision.Colors -import hep.dataforge.vision.layout.Output import hep.dataforge.vision.solid.Solid import hep.dataforge.vision.solid.specifications.* import hep.dataforge.vision.solid.three.ThreeMaterials.HIGHLIGHT_MATERIAL @@ -39,7 +36,7 @@ import kotlin.math.sin public class ThreeCanvas( public val three: ThreePlugin, public val options: Canvas3DOptions, -) : Output { +) { private var root: Object3D? = null 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 { //Throw error is something is already rendered here error("Root object already is present in the canvas") diff --git a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreePlugin.kt b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreePlugin.kt index 080a71f7..8468ba53 100644 --- a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreePlugin.kt +++ b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreePlugin.kt @@ -4,14 +4,13 @@ import hep.dataforge.context.* import hep.dataforge.meta.Meta import hep.dataforge.names.* import hep.dataforge.vision.Vision +import hep.dataforge.vision.VisionForge import hep.dataforge.vision.client.ElementVisionRenderer import hep.dataforge.vision.solid.* import hep.dataforge.vision.solid.specifications.Canvas3DOptions import hep.dataforge.vision.visible import info.laht.threekt.core.Object3D import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach import org.w3c.dom.Element import org.w3c.dom.HTMLElement 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( element: HTMLElement, obj: Solid, diff --git a/visionforge-threejs/visionforge-threejs-server/src/jsMain/kotlin/hep/dataforge/vision/three/server/jsMain.kt b/visionforge-threejs/visionforge-threejs-server/src/jsMain/kotlin/hep/dataforge/vision/three/server/jsMain.kt index 86ad4ecc..ab4b5e46 100644 --- a/visionforge-threejs/visionforge-threejs-server/src/jsMain/kotlin/hep/dataforge/vision/three/server/jsMain.kt +++ b/visionforge-threejs/visionforge-threejs-server/src/jsMain/kotlin/hep/dataforge/vision/three/server/jsMain.kt @@ -1,20 +1,9 @@ package hep.dataforge.vision.three.server -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 +import hep.dataforge.vision.renderVisionsInWindow +import hep.dataforge.vision.solid.three.loadThreeJs public fun main() { - //Loading three-js renderer - val visionContext = Global.context("threejs") { - plugin(ThreePlugin) - } - val clientManager = visionContext.plugins.fetch(VisionClient) - - //Fetch from server and render visions for all outputs - window.onload = { - clientManager.renderAllVisions() - } + loadThreeJs() + renderVisionsInWindow() } \ No newline at end of file diff --git a/visionforge-threejs/visionforge-threejs-server/src/jvmMain/kotlin/hep/dataforge/vision/three/server/serverExtensions.kt b/visionforge-threejs/visionforge-threejs-server/src/jvmMain/kotlin/hep/dataforge/vision/three/server/serverExtensions.kt index ca20c709..d44fa02e 100644 --- a/visionforge-threejs/visionforge-threejs-server/src/jvmMain/kotlin/hep/dataforge/vision/three/server/serverExtensions.kt +++ b/visionforge-threejs/visionforge-threejs-server/src/jvmMain/kotlin/hep/dataforge/vision/three/server/serverExtensions.kt @@ -2,10 +2,12 @@ package hep.dataforge.vision.three.server import hep.dataforge.context.Context import hep.dataforge.meta.DFExperimental -import hep.dataforge.vision.ResourceLocation import hep.dataforge.vision.html.HtmlVisionFragment -import hep.dataforge.vision.makeVisionFile -import hep.dataforge.vision.scriptHeader +import hep.dataforge.vision.html.ResourceLocation +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 @@ -14,12 +16,15 @@ public fun VisionServer.useThreeJs(): Unit { } @DFExperimental -public fun Context.makeVisionFile( - fragment: HtmlVisionFragment, +public fun Context.makeThreeJsFile( + content: 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-three.js", actualPath, resourceLocation) -} \ No newline at end of file +): Unit { + 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()) +}