diff --git a/build.gradle.kts b/build.gradle.kts index 9bdf18a6..65e355ac 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -22,7 +22,7 @@ allprojects { } group = "space.kscience" - version = "0.2.0-dev-16" + version = "0.2.0-dev-17" } subprojects { diff --git a/demo/playground/build.gradle.kts b/demo/playground/build.gradle.kts index 733bc7c2..7c99d6fa 100644 --- a/demo/playground/build.gradle.kts +++ b/demo/playground/build.gradle.kts @@ -14,10 +14,15 @@ repositories{ kotlin { js(IR) { + useCommonJs() browser { webpackTask { this.outputFileName = "js/visionforge-playground.js" } + commonWebpackConfig { + sourceMaps = false + cssSupport.enabled = false + } } binaries.executable() } @@ -54,7 +59,7 @@ kotlin { val jsMain by getting{ dependencies { - api(project(":ui:bootstrap")) + implementation(project(":ui:ring")) api(project(":visionforge-threejs")) } } diff --git a/demo/playground/src/jsMain/kotlin/playgroundMain.kt b/demo/playground/src/jsMain/kotlin/playgroundMain.kt index 9ee2dc93..42cb97e2 100644 --- a/demo/playground/src/jsMain/kotlin/playgroundMain.kt +++ b/demo/playground/src/jsMain/kotlin/playgroundMain.kt @@ -1,10 +1,10 @@ import space.kscience.dataforge.misc.DFExperimental import space.kscience.visionforge.plotly.PlotlyPlugin +import space.kscience.visionforge.ring.ThreeWithControls import space.kscience.visionforge.runVisionClient -import space.kscience.visionforge.solid.three.ThreePlugin @DFExperimental fun main() = runVisionClient { plugin(PlotlyPlugin) - plugin(ThreePlugin) + plugin(ThreeWithControls) } \ No newline at end of file diff --git a/demo/playground/src/jvmMain/kotlin/gdmCurve.kt b/demo/playground/src/jvmMain/kotlin/gdmCurve.kt index 2b9aef01..3f7ff281 100644 --- a/demo/playground/src/jvmMain/kotlin/gdmCurve.kt +++ b/demo/playground/src/jvmMain/kotlin/gdmCurve.kt @@ -3,14 +3,17 @@ package space.kscience.visionforge.examples import space.kscience.dataforge.context.Context import space.kscience.gdml.* import space.kscience.visionforge.gdml.toVision +import space.kscience.visionforge.html.ResourceLocation import space.kscience.visionforge.solid.Solids +import space.kscience.visionforge.visible +import java.nio.file.Path fun main() { val context = Context { plugin(Solids) } - context.makeVisionFile { + context.makeVisionFile(Path.of("curves.html"), resourceLocation = ResourceLocation.EMBED) { vision("canvas") { Gdml { // geometry variables @@ -39,7 +42,7 @@ fun main() { structure { val worldMaterial = materials.composite("G4_AIR") - val worldBox = solids.box(worldSize, worldSize, worldSize) + val worldBox = solids.box(worldSize, worldSize, worldSize, name = "world") val shieldingMaterial = materials.composite("G4_Pb") val scintillatorMaterial = materials.composite("BC408") @@ -221,7 +224,14 @@ fun main() { } } }.toVision { - this.solidAction + configure { parent, solid, material -> + //disable visibility for the world box + if(solid.name == "world"){ + visible = false + } + //make all solids semi-transparent + transparent() + } } } } diff --git a/demo/playground/webpack.config.d/01.ring.js b/demo/playground/webpack.config.d/01.ring.js new file mode 100644 index 00000000..41da041c --- /dev/null +++ b/demo/playground/webpack.config.d/01.ring.js @@ -0,0 +1,3 @@ +const ringConfig = require('@jetbrains/ring-ui/webpack.config').config; + +config.module.rules.push(...ringConfig.module.rules) \ No newline at end of file diff --git a/jupyter/visionforge-gdml-jupyter/build.gradle.kts b/jupyter/visionforge-gdml-jupyter/build.gradle.kts index 86847c2c..48391b5d 100644 --- a/jupyter/visionforge-gdml-jupyter/build.gradle.kts +++ b/jupyter/visionforge-gdml-jupyter/build.gradle.kts @@ -8,10 +8,15 @@ description = "Jupyter api artifact for GDML rendering" kotlin{ explicitApi = null js{ + useCommonJs() browser { webpackTask { this.outputFileName = "js/gdml-jupyter.js" } + commonWebpackConfig { + sourceMaps = false + cssSupport.enabled = false + } } binaries.executable() } @@ -42,7 +47,7 @@ kotlin{ jsMain { dependencies { api(project(":visionforge-threejs")) - implementation(project(":ui:bootstrap")) + implementation(project(":ui:ring")) } } diff --git a/jupyter/visionforge-gdml-jupyter/src/jsMain/kotlin/gdmlJupyter.kt b/jupyter/visionforge-gdml-jupyter/src/jsMain/kotlin/gdmlJupyter.kt index 9c4d8140..3356c20d 100644 --- a/jupyter/visionforge-gdml-jupyter/src/jsMain/kotlin/gdmlJupyter.kt +++ b/jupyter/visionforge-gdml-jupyter/src/jsMain/kotlin/gdmlJupyter.kt @@ -1,27 +1,12 @@ package space.kscience.visionforge.gdml.jupyter -import kotlinx.css.ListStyleType -import kotlinx.css.listStyleType import space.kscience.dataforge.misc.DFExperimental -import space.kscience.visionforge.bootstrap.useBootstrap +import space.kscience.visionforge.ring.ThreeWithControls import space.kscience.visionforge.runVisionClient -import styled.injectGlobal @DFExperimental +@JsExport fun main(): Unit = runVisionClient { - useBootstrap() - injectGlobal { - rule("ul.nav") { - listStyleType = ListStyleType.none - } - - rule(".treeStyles-tree") { - listStyleType = ListStyleType.none - } - - rule("ol.breadcrumb") { - listStyleType = ListStyleType.none - } - } plugin(ThreeWithControls) -} \ No newline at end of file +} + diff --git a/jupyter/visionforge-gdml-jupyter/src/jsMain/kotlin/threeViewer.kt b/jupyter/visionforge-gdml-jupyter/src/jsMain/kotlin/threeViewer.kt deleted file mode 100644 index d7818254..00000000 --- a/jupyter/visionforge-gdml-jupyter/src/jsMain/kotlin/threeViewer.kt +++ /dev/null @@ -1,83 +0,0 @@ -package space.kscience.visionforge.gdml.jupyter - -import kotlinx.css.* -import react.RProps -import react.child -import react.dom.h1 -import react.functionalComponent -import react.useState -import space.kscience.dataforge.context.Context -import space.kscience.dataforge.names.Name -import space.kscience.visionforge.Vision -import space.kscience.visionforge.bootstrap.gridRow -import space.kscience.visionforge.bootstrap.nameCrumbs -import space.kscience.visionforge.bootstrap.threeControls -import space.kscience.visionforge.react.ThreeCanvasComponent -import space.kscience.visionforge.react.flexColumn -import space.kscience.visionforge.solid.Solid -import space.kscience.visionforge.solid.specifications.Canvas3DOptions -import space.kscience.visionforge.solid.three.ThreeCanvas -import styled.css -import styled.styledDiv - -external interface GdmlViewProps : RProps { - var context: Context - var rootVision: Vision? - var selected: Name? -} - -@JsExport -val GdmlView = functionalComponent("GdmlView") { props -> - var selected by useState { props.selected } - var canvas: ThreeCanvas? by useState { null } - var vision: Vision? by useState { props.rootVision } - - val onSelect: (Name?) -> Unit = { - selected = it - } - - gridRow { - flexColumn { - css { - +"col-lg-9" - height = 100.vh - } - styledDiv { - css { - +"mx-auto" - +"page-header" - } - h1 { +"GDML/JSON loader demo" } - } - nameCrumbs(selected, "World", onSelect) - //canvas - - child(ThreeCanvasComponent) { - attrs { - this.context = props.context - this.obj = vision as? Solid - this.selected = selected - this.options = Canvas3DOptions.invoke { - this.onSelect = onSelect - } - this.canvasCallback = { - canvas = it - } - } - } - - } - flexColumn { - css { - +"col-lg-3" - padding(top = 4.px) - //border(1.px, BorderStyle.solid, Color.lightGray) - height = 100.vh - overflowY = Overflow.auto - } - canvas?.let { - threeControls(it, selected, onSelect) - } - } - } -} \ No newline at end of file diff --git a/jupyter/visionforge-gdml-jupyter/src/jvmMain/kotlin/GdmlForJupyter.kt b/jupyter/visionforge-gdml-jupyter/src/jvmMain/kotlin/GdmlForJupyter.kt index 31d1d9aa..0b12d3fb 100644 --- a/jupyter/visionforge-gdml-jupyter/src/jvmMain/kotlin/GdmlForJupyter.kt +++ b/jupyter/visionforge-gdml-jupyter/src/jvmMain/kotlin/GdmlForJupyter.kt @@ -36,9 +36,6 @@ internal class GdmlForJupyter : JupyterIntegration() { js("three") { classPath("js/gdml-jupyter.js") } -// css("override") { -// classPath("css/jupyter-override.css") -// } } import( diff --git a/jupyter/visionforge-gdml-jupyter/webpack.config.d/01.ring.js b/jupyter/visionforge-gdml-jupyter/webpack.config.d/01.ring.js new file mode 100644 index 00000000..41da041c --- /dev/null +++ b/jupyter/visionforge-gdml-jupyter/webpack.config.d/01.ring.js @@ -0,0 +1,3 @@ +const ringConfig = require('@jetbrains/ring-ui/webpack.config').config; + +config.module.rules.push(...ringConfig.module.rules) \ No newline at end of file diff --git a/ui/react/src/main/kotlin/space/kscience/visionforge/react/ThreeCanvasComponent.kt b/ui/react/src/main/kotlin/space/kscience/visionforge/react/ThreeCanvasComponent.kt index 74a1a61d..7446acce 100644 --- a/ui/react/src/main/kotlin/space/kscience/visionforge/react/ThreeCanvasComponent.kt +++ b/ui/react/src/main/kotlin/space/kscience/visionforge/react/ThreeCanvasComponent.kt @@ -2,6 +2,8 @@ package space.kscience.visionforge.react import kotlinx.css.Display import kotlinx.css.display +import kotlinx.css.height +import kotlinx.css.pct import org.w3c.dom.Element import org.w3c.dom.HTMLElement import react.* @@ -60,6 +62,7 @@ public val ThreeCanvasComponent: FunctionalComponent = functio styledDiv { css { display = Display.contents + height = 100.pct } ref = elementRef } diff --git a/ui/ring/build.gradle.kts b/ui/ring/build.gradle.kts index 5bd5817c..54d6b737 100644 --- a/ui/ring/build.gradle.kts +++ b/ui/ring/build.gradle.kts @@ -21,6 +21,7 @@ dependencies{ implementation(npm("@jetbrains/icons", "3.14.1")) implementation(npm("@jetbrains/ring-ui", "4.0.7")) implementation(npm("core-js","3.12.1")) + implementation(npm("file-saver", "2.0.2")) compileOnly(npm("url-loader","4.1.1")) compileOnly(npm("postcss-loader","5.2.0")) compileOnly(npm("source-map-loader","2.0.1")) diff --git a/ui/ring/src/main/kotlin/ringui/grid/Col.kt b/ui/ring/src/main/kotlin/ringui/grid/Col.kt new file mode 100644 index 00000000..057415db --- /dev/null +++ b/ui/ring/src/main/kotlin/ringui/grid/Col.kt @@ -0,0 +1,23 @@ +package ringui.grid + +import react.RBuilder +import react.RHandler +import react.dom.WithClassName + +public external interface ColProps : WithClassName { + public var xs: dynamic // number or boolean + public var sm: dynamic // number or boolean + public var md: dynamic // number or boolean + public var lg: dynamic // number or boolean + public var xsOffset: Number + public var smOffset: Number + public var mdOffset: Number + public var lgOffset: Number + public var reverse: Boolean +} + +public fun RBuilder.ringCol(handler: RHandler){ + GridModule.Col { + handler() + } +} \ No newline at end of file diff --git a/ui/ring/src/main/kotlin/ringui/grid/Grid.kt b/ui/ring/src/main/kotlin/ringui/grid/Grid.kt new file mode 100644 index 00000000..7613a6c4 --- /dev/null +++ b/ui/ring/src/main/kotlin/ringui/grid/Grid.kt @@ -0,0 +1,20 @@ +package ringui.grid + +import react.RBuilder +import react.RClass +import react.RHandler +import react.RProps + +@JsModule("@jetbrains/ring-ui/components/grid/grid") +internal external object GridModule { + val Grid: RClass + val Row: RClass + val Col: RClass +} + + +public fun RBuilder.ringGrid(handler: RHandler) { + GridModule.Grid { + handler() + } +} \ No newline at end of file diff --git a/ui/ring/src/main/kotlin/ringui/grid/Row.kt b/ui/ring/src/main/kotlin/ringui/grid/Row.kt new file mode 100644 index 00000000..78131971 --- /dev/null +++ b/ui/ring/src/main/kotlin/ringui/grid/Row.kt @@ -0,0 +1,33 @@ +package ringui.grid + +import react.RBuilder +import react.RHandler +import react.dom.WithClassName + +public enum class RowPosition { + xs, + sm, + md, + lg +} + +public external interface RowProps : WithClassName { + public var reverse: Boolean + public var start: RowPosition + public var center: RowPosition + public var end: RowPosition + public var top: RowPosition + public var middle: RowPosition + public var baseline: RowPosition + public var bottom: RowPosition + public var around: RowPosition + public var between: RowPosition + public var first: RowPosition + public var last: RowPosition +} + +public fun RBuilder.ringRow(handler: RHandler){ + GridModule.Row { + handler() + } +} \ No newline at end of file diff --git a/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ThreeViewWithControls.kt b/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ThreeViewWithControls.kt new file mode 100644 index 00000000..2b1dca09 --- /dev/null +++ b/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ThreeViewWithControls.kt @@ -0,0 +1,87 @@ +package space.kscience.visionforge.ring + +import kotlinx.css.* +import react.RProps +import react.child +import react.functionalComponent +import react.useState +import ringui.grid.ringCol +import ringui.grid.ringGrid +import ringui.grid.ringRow +import space.kscience.dataforge.context.Context +import space.kscience.dataforge.names.Name +import space.kscience.visionforge.Vision +import space.kscience.visionforge.react.ThreeCanvasComponent +import space.kscience.visionforge.solid.Solid +import space.kscience.visionforge.solid.specifications.Canvas3DOptions +import space.kscience.visionforge.solid.three.ThreeCanvas +import styled.css +import styled.styledDiv + +public external interface GdmlViewProps : RProps { + public var context: Context + public var rootVision: Vision? + public var selected: Name? +} + +@JsExport +public val ThreeViewWithControls: (props: GdmlViewProps) -> dynamic = + functionalComponent("ThreeViewWithControls") { props -> + var selected by useState { props.selected } + var canvas: ThreeCanvas? by useState { null } + + val onSelect: (Name?) -> Unit = { + selected = it + } + + styledDiv { + css { + height = 100.pct + } + ringGrid { + ringRow { + ringCol { + attrs { + xs = 12 + sm = 12 + md = 8 + lg = 9 + } + child(ThreeCanvasComponent) { + attrs { + this.context = props.context + this.obj = props.rootVision as? Solid + this.selected = selected + this.options = Canvas3DOptions.invoke { + this.onSelect = onSelect + } + this.canvasCallback = { + canvas = it + } + } + } + + } + ringCol { + attrs { + xs = 12 + sm = 12 + md = 4 + lg = 3 + } + styledDiv { + css { + padding(top = 4.px) + //border(1.px, BorderStyle.solid, Color.lightGray) + height = 100.pct + overflowY = Overflow.auto + } + canvas?.let { + ringThreeControls(it, selected, onSelect) + } + } + } + } + } + } + } \ No newline at end of file diff --git a/jupyter/visionforge-gdml-jupyter/src/jsMain/kotlin/ThreeWithControls.kt b/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ThreeWithControls.kt similarity index 85% rename from jupyter/visionforge-gdml-jupyter/src/jsMain/kotlin/ThreeWithControls.kt rename to ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ThreeWithControls.kt index bad07723..9b415e84 100644 --- a/jupyter/visionforge-gdml-jupyter/src/jsMain/kotlin/ThreeWithControls.kt +++ b/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ThreeWithControls.kt @@ -1,4 +1,4 @@ -package space.kscience.visionforge.gdml.jupyter +package space.kscience.visionforge.ring import org.w3c.dom.Element import react.child @@ -15,8 +15,8 @@ import space.kscience.visionforge.solid.Solid import space.kscience.visionforge.solid.three.ThreePlugin import kotlin.reflect.KClass -class ThreeWithControls : AbstractPlugin(), ElementVisionRenderer { - val three by require(ThreePlugin) +public class ThreeWithControls : AbstractPlugin(), ElementVisionRenderer { + public val three by require(ThreePlugin) override val tag: PluginTag get() = Companion.tag @@ -25,7 +25,7 @@ class ThreeWithControls : AbstractPlugin(), ElementVisionRenderer { override fun render(element: Element, vision: Vision, meta: Meta) { react.dom.render(element) { - child(GdmlView) { + child(ThreeViewWithControls) { attrs { this.context = this@ThreeWithControls.context this.rootVision = vision @@ -41,7 +41,7 @@ class ThreeWithControls : AbstractPlugin(), ElementVisionRenderer { } } - companion object : PluginFactory { + public companion object : PluginFactory { override val tag: PluginTag = PluginTag("vision.threejs.withControls", PluginTag.DATAFORGE_GROUP) override val type: KClass = ThreeWithControls::class override fun invoke(meta: Meta, context: Context): ThreeWithControls = ThreeWithControls() diff --git a/visionforge-core/src/jsMain/kotlin/space/kscience/visionforge/VisionClient.kt b/visionforge-core/src/jsMain/kotlin/space/kscience/visionforge/VisionClient.kt index 8cd5dd09..22d670dd 100644 --- a/visionforge-core/src/jsMain/kotlin/space/kscience/visionforge/VisionClient.kt +++ b/visionforge-core/src/jsMain/kotlin/space/kscience/visionforge/VisionClient.kt @@ -217,10 +217,7 @@ public fun VisionClient.renderAllVisions(): Unit = whenDocumentLoaded { */ public fun runVisionClient(contextBuilder: ContextBuilder.() -> Unit) { console.info("Starting VisionForge context") - val context = Context("VisionForge"){ - contextBuilder() - //plugin(VisionClient) - } + val context = Context("VisionForge", contextBuilder) val visionClient = context.fetch(VisionClient) window.asDynamic()[RENDER_FUNCTION_NAME] = visionClient::renderAllVisionsById diff --git a/visionforge-core/src/jvmMain/kotlin/space/kscience/visionforge/html/headers.kt b/visionforge-core/src/jvmMain/kotlin/space/kscience/visionforge/html/headers.kt index 56b2e17b..ffe35cc7 100644 --- a/visionforge-core/src/jvmMain/kotlin/space/kscience/visionforge/html/headers.kt +++ b/visionforge-core/src/jvmMain/kotlin/space/kscience/visionforge/html/headers.kt @@ -3,6 +3,7 @@ package space.kscience.visionforge.html import kotlinx.html.link import kotlinx.html.script import kotlinx.html.unsafe +import org.slf4j.LoggerFactory import space.kscience.dataforge.misc.DFExperimental import space.kscience.visionforge.VisionManager import java.nio.file.Files @@ -49,18 +50,22 @@ private fun ByteArray.toHexString() = asUByteArray().joinToString("") { it.toStr */ @OptIn(ExperimentalPathApi::class) internal fun checkOrStoreFile(htmlPath: Path, filePath: Path, resource: String): Path { - //TODO add logging - val fullPath = htmlPath.resolveSibling(filePath).toAbsolutePath().resolve(resource) + val logger = LoggerFactory.getLogger("") - val bytes = VisionManager::class.java.getResourceAsStream("/$resource").readAllBytes() + logger.info("Resolving or storing resource file $resource") + val fullPath = htmlPath.resolveSibling(filePath).toAbsolutePath().resolve(resource) + logger.debug("Full path to resource file $resource: $fullPath") + + val bytes = VisionManager.Companion::class.java.getResourceAsStream("/$resource")?.readAllBytes() + ?: error("Resource $resource not found on classpath") val md = MessageDigest.getInstance("MD5") val checksum = md.digest(bytes).toHexString() - val md5File = fullPath.resolveSibling(fullPath.fileName.toString() + ".md5") val skip: Boolean = Files.exists(fullPath) && Files.exists(md5File) && md5File.readText() == checksum if (!skip) { + logger.debug("File $fullPath does not exist or wrong checksum. Writing file") Files.createDirectories(fullPath.parent) Files.write(fullPath, bytes, StandardOpenOption.CREATE, StandardOpenOption.WRITE) Files.write(md5File, checksum.encodeToByteArray(), StandardOpenOption.CREATE, StandardOpenOption.WRITE) diff --git a/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/GdmlTransformerEnv.kt b/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/GdmlTransformerEnv.kt index c4a2364a..0d4b6d29 100644 --- a/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/GdmlTransformerEnv.kt +++ b/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/GdmlTransformerEnv.kt @@ -48,8 +48,8 @@ public class GdmlTransformer { useStyle(name) } - public fun Solid.opaque() { - useStyle("opaque") { + public fun Solid.transparent() { + useStyle("transparent") { SolidMaterial.MATERIAL_OPACITY_KEY put 0.3 "edges.enabled" put true } @@ -73,7 +73,7 @@ public class GdmlTransformer { { parent, solid, material -> val styleName = "materials.${material.name}" - if (parent.physVolumes.isNotEmpty()) opaque() + if (parent.physVolumes.isNotEmpty()) transparent() useStyle(styleName) { val vfMaterial = SolidMaterial().apply { @@ -85,6 +85,13 @@ public class GdmlTransformer { } private set + public fun configure(block: Solid.(parent: GdmlVolume, solid: GdmlSolid, material: GdmlMaterial) -> Unit) { + val oldConfigure = configureSolid + configureSolid = { parent: GdmlVolume, solid: GdmlSolid, material: GdmlMaterial -> + oldConfigure(parent, solid, material) + block(parent, solid, material) + } + } public companion object { diff --git a/visionforge-threejs/build.gradle.kts b/visionforge-threejs/build.gradle.kts index 498c63f8..68ab9204 100644 --- a/visionforge-threejs/build.gradle.kts +++ b/visionforge-threejs/build.gradle.kts @@ -3,6 +3,7 @@ plugins { } kotlin{ + explicitApi = null js{ binaries.library() } diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCanvas.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCanvas.kt index 8d0e0d0d..e9c7a61a 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCanvas.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCanvas.kt @@ -110,7 +110,6 @@ public class ThreeCanvas( width = "100%" height = "100%" display = "block" - zIndex = "1000" } }