From faf3fa85124ddba153363539cda4f8dc4aef3986 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 8 May 2021 23:14:15 +0300 Subject: [PATCH] Add multi-select value chooser for layers --- demo/gdml/src/jsMain/resources/index.html | 1 - .../playground/src/jvmMain/kotlin/gdmCurve.kt | 6 +- .../playground/src/jvmMain/kotlin/gdmlIaxo.kt | 6 +- .../src/jvmMain/kotlin/GdmlForJupyter.kt | 35 ++--- ui/react/build.gradle.kts | 1 + .../visionforge/react/MultiSelectChooser.kt | 42 ++++++ .../visionforge/react/valueChooser.kt | 1 + .../kscience/visionforge/StyleReference.kt | 29 ++-- ...ticHtmlRender.kt => HtmlVisionRenderer.kt} | 26 ++-- .../visionforge/html/VisionTagConsumer.kt | 4 +- .../kscience/visionforge/schemeDesctiptors.kt | 6 +- .../kscience/visionforge/html/HtmlTagTest.kt | 28 +++- .../kscience/visionforge/VisionClient.kt | 3 +- ...mlTransformer.kt => GdmlTransformerEnv.kt} | 134 +++++++++++------- .../kscience/visionforge/gdml/gdmlJVM.kt | 2 +- .../visionforge/three/server/VisionServer.kt | 2 +- .../visionforge/solid/specifications/Axes.kt | 12 +- .../solid/specifications/Camera.kt | 24 +++- .../solid/specifications/Canvas3DOptions.kt | 44 ++++-- .../visionforge/solid/three/ThreeCanvas.kt | 41 +++--- 20 files changed, 295 insertions(+), 152 deletions(-) create mode 100644 ui/react/src/main/kotlin/space/kscience/visionforge/react/MultiSelectChooser.kt rename visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/{staticHtmlRender.kt => HtmlVisionRenderer.kt} (65%) rename visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/{GdmlTransformer.kt => GdmlTransformerEnv.kt} (84%) diff --git a/demo/gdml/src/jsMain/resources/index.html b/demo/gdml/src/jsMain/resources/index.html index ba6c6868..8f5ca1ef 100644 --- a/demo/gdml/src/jsMain/resources/index.html +++ b/demo/gdml/src/jsMain/resources/index.html @@ -5,7 +5,6 @@ Three js demo for particle physics - diff --git a/demo/playground/src/jvmMain/kotlin/gdmCurve.kt b/demo/playground/src/jvmMain/kotlin/gdmCurve.kt index 12981241..2b9aef01 100644 --- a/demo/playground/src/jvmMain/kotlin/gdmCurve.kt +++ b/demo/playground/src/jvmMain/kotlin/gdmCurve.kt @@ -2,7 +2,7 @@ package space.kscience.visionforge.examples import space.kscience.dataforge.context.Context import space.kscience.gdml.* -import space.kscience.visionforge.gdml.gdml +import space.kscience.visionforge.gdml.toVision import space.kscience.visionforge.solid.Solids fun main() { @@ -12,7 +12,7 @@ fun main() { context.makeVisionFile { vision("canvas") { - gdml { + Gdml { // geometry variables val worldSize = 500 // chamber @@ -220,6 +220,8 @@ fun main() { } } } + }.toVision { + this.solidAction } } } diff --git a/demo/playground/src/jvmMain/kotlin/gdmlIaxo.kt b/demo/playground/src/jvmMain/kotlin/gdmlIaxo.kt index 10e81070..a9070af7 100644 --- a/demo/playground/src/jvmMain/kotlin/gdmlIaxo.kt +++ b/demo/playground/src/jvmMain/kotlin/gdmlIaxo.kt @@ -10,9 +10,7 @@ fun main() { plugin(Solids) } - context.makeVisionFile{ - vision("canvas") { - GdmlShowCase.babyIaxo().toVision() - } + context.makeVisionFile { + vision("canvas") { GdmlShowCase.babyIaxo().toVision() } } } \ 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 86041d32..31d1d9aa 100644 --- a/jupyter/visionforge-gdml-jupyter/src/jvmMain/kotlin/GdmlForJupyter.kt +++ b/jupyter/visionforge-gdml-jupyter/src/jvmMain/kotlin/GdmlForJupyter.kt @@ -1,10 +1,6 @@ package space.kscience.visionforge.gdml.jupyter -import kotlinx.html.div -import kotlinx.html.id -import kotlinx.html.script import kotlinx.html.stream.createHTML -import kotlinx.html.unsafe import org.jetbrains.kotlinx.jupyter.api.HTML import org.jetbrains.kotlinx.jupyter.api.annotations.JupyterLibrary import org.jetbrains.kotlinx.jupyter.api.libraries.JupyterIntegration @@ -16,7 +12,7 @@ import space.kscience.visionforge.Vision import space.kscience.visionforge.gdml.toVision import space.kscience.visionforge.html.HtmlVisionFragment import space.kscience.visionforge.html.Page -import space.kscience.visionforge.html.embedVisionFragment +import space.kscience.visionforge.html.embedAndRenderVisionFragment import space.kscience.visionforge.solid.Solids import space.kscience.visionforge.visionManager @@ -30,17 +26,9 @@ internal class GdmlForJupyter : JupyterIntegration() { private var counter = 0 - private fun produceHtmlVisionString(fragment: HtmlVisionFragment) = createHTML().div { - val id = "visionforge.vision[${counter++}]" - div { - this.id = id - embedVisionFragment(context.visionManager, fragment = fragment) - } - script { - type = "text/javascript" - unsafe { +"VisionForge.renderVisionsAt(\"$id\");" } - } - } + private fun produceHtmlVisionString(fragment: HtmlVisionFragment) = createHTML().apply { + embedAndRenderVisionFragment(context.visionManager, counter++, fragment) + }.finalize() override fun Builder.onLoaded() { @@ -62,19 +50,12 @@ internal class GdmlForJupyter : JupyterIntegration() { "space.kscience.visionforge.gdml.jupyter.*" ) - render { gdmlModel -> - val fragment = HtmlVisionFragment { - vision(gdmlModel.toVision()) - } - HTML(produceHtmlVisionString(fragment)) + render { vision -> + HTML(produceHtmlVisionString { vision(vision) }) } - render { vision -> - val fragment = HtmlVisionFragment { - vision(vision) - } - - HTML(produceHtmlVisionString(fragment)) + render { gdmlModel -> + HTML(produceHtmlVisionString { vision(gdmlModel.toVision()) }) } render { page -> diff --git a/ui/react/build.gradle.kts b/ui/react/build.gradle.kts index 86c5068f..f20a63b8 100644 --- a/ui/react/build.gradle.kts +++ b/ui/react/build.gradle.kts @@ -8,5 +8,6 @@ dependencies{ api(project(":visionforge-solid")) api("org.jetbrains:kotlin-styled:5.2.3-$kotlinWrappersVersion") api("org.jetbrains:kotlin-react-dom:17.0.2-$kotlinWrappersVersion") +// implementation(npm("react-select","4.3.0")) implementation(project(":visionforge-threejs")) } \ No newline at end of file diff --git a/ui/react/src/main/kotlin/space/kscience/visionforge/react/MultiSelectChooser.kt b/ui/react/src/main/kotlin/space/kscience/visionforge/react/MultiSelectChooser.kt new file mode 100644 index 00000000..497256c8 --- /dev/null +++ b/ui/react/src/main/kotlin/space/kscience/visionforge/react/MultiSelectChooser.kt @@ -0,0 +1,42 @@ +package space.kscience.visionforge.react + +import kotlinx.html.js.onChangeFunction +import org.w3c.dom.HTMLOptionElement +import org.w3c.dom.HTMLSelectElement +import org.w3c.dom.asList +import org.w3c.dom.events.Event +import react.FunctionalComponent +import react.dom.option +import react.dom.select +import react.functionalComponent +import react.useState +import space.kscience.dataforge.meta.value +import space.kscience.dataforge.values.asValue +import space.kscience.dataforge.values.string + +@JsExport +public val MultiSelectChooser: FunctionalComponent = + functionalComponent("MultiSelectChooser") { props -> + var selectedItems by useState { props.item.value?.list ?: emptyList() } + + val onChange: (Event) -> Unit = { event: Event -> + val newSelected= (event.target as HTMLSelectElement).selectedOptions.asList() + .map { (it as HTMLOptionElement).value.asValue() } + props.valueChanged?.invoke(newSelected.asValue()) + selectedItems = newSelected + } + + select { + attrs { + multiple = true + values = selectedItems.mapTo(HashSet()) { it.string } + onChangeFunction = onChange + } + props.descriptor?.allowedValues?.forEach { optionValue -> + option { + +optionValue.string + } + } + + } + } \ No newline at end of file diff --git a/ui/react/src/main/kotlin/space/kscience/visionforge/react/valueChooser.kt b/ui/react/src/main/kotlin/space/kscience/visionforge/react/valueChooser.kt index feab99d8..b0b448bb 100644 --- a/ui/react/src/main/kotlin/space/kscience/visionforge/react/valueChooser.kt +++ b/ui/react/src/main/kotlin/space/kscience/visionforge/react/valueChooser.kt @@ -152,6 +152,7 @@ public val ValueChooser: FunctionalComponent = functionalComp when { rawInput -> child(StringValueChooser, props) descriptor?.widgetType == "color" -> child(ColorValueChooser, props) + descriptor?.widgetType == "multiSelect" -> child(MultiSelectChooser, props) type == ValueType.BOOLEAN -> child(BooleanValueChooser, props) type == ValueType.NUMBER -> child(NumberValueChooser, props) descriptor?.allowedValues?.isNotEmpty() ?: false -> child(ComboValueChooser, props) diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/StyleReference.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/StyleReference.kt index e25315aa..961e937a 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/StyleReference.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/StyleReference.kt @@ -2,7 +2,8 @@ package space.kscience.visionforge import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.MetaBuilder -import space.kscience.dataforge.misc.DFExperimental +import space.kscience.dataforge.meta.Scheme +import space.kscience.dataforge.meta.Specification import kotlin.properties.ReadOnlyProperty /** @@ -23,11 +24,23 @@ public fun Vision.useStyle(reference: StyleReference) { useStyle(reference.name) } -@DFExperimental @VisionBuilder -public fun VisionGroup.style(builder: MetaBuilder.() -> Unit): ReadOnlyProperty = - ReadOnlyProperty { _, property -> - val styleName = property.name - styleSheet.define(styleName, Meta(builder)) - StyleReference(this, styleName) - } \ No newline at end of file +public fun VisionGroup.style( + styleKey: String? = null, + builder: MetaBuilder.() -> Unit, +): ReadOnlyProperty = ReadOnlyProperty { _, property -> + val styleName = styleKey ?: property.name + styleSheet.define(styleName, Meta(builder)) + StyleReference(this, styleName) +} + +@VisionBuilder +public fun VisionGroup.style( + spec: Specification, + styleKey: String? = null, + builder: T.() -> Unit, +): ReadOnlyProperty = ReadOnlyProperty { _, property -> + val styleName = styleKey ?: property.name + styleSheet.define(styleName, spec(builder).toMeta()) + StyleReference(this, styleName) +} \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/staticHtmlRender.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/HtmlVisionRenderer.kt similarity index 65% rename from visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/staticHtmlRender.kt rename to visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/HtmlVisionRenderer.kt index e246572e..2e902d4d 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/staticHtmlRender.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/HtmlVisionRenderer.kt @@ -2,6 +2,7 @@ package space.kscience.visionforge.html import kotlinx.html.* import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.names.Name import space.kscience.visionforge.Vision import space.kscience.visionforge.VisionManager @@ -35,20 +36,19 @@ public fun FlowContent.embedVisionFragment( fragment: HtmlVisionFragment, ): Map = consumer.embedVisionFragment(manager, idPrefix, fragment) -public typealias HtmlVisionRenderer = FlowContent.(name: Name, vision: Vision, meta: Meta) -> Unit -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) { - visionMap[name] = vision - renderer(name, vision, outputMeta) +internal const val RENDER_FUNCTION_NAME = "renderAllVisionsById" + +@DFExperimental +public fun TagConsumer<*>.embedAndRenderVisionFragment(manager: VisionManager, id: Any, fragment: HtmlVisionFragment){ + div { + div { + this.id = id.toString() + embedVisionFragment(manager, fragment = fragment) + } + script { + type = "text/javascript" + unsafe { +"window.${RENDER_FUNCTION_NAME}(\"$id\");" } } } - fragment(consumer) - return visionMap } \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/VisionTagConsumer.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/VisionTagConsumer.kt index aa8e6fcb..967e1851 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/VisionTagConsumer.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/VisionTagConsumer.kt @@ -66,7 +66,9 @@ public abstract class VisionTagConsumer( } } } - vision?.let { renderVision(name, it, outputMeta) } + vision?.let { + renderVision(name, it, outputMeta) + } } @OptIn(DFExperimental::class) diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/schemeDesctiptors.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/schemeDesctiptors.kt index f1a5fb1c..8c43d0e0 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/schemeDesctiptors.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/schemeDesctiptors.kt @@ -4,7 +4,7 @@ import space.kscience.dataforge.meta.Scheme import space.kscience.dataforge.meta.SchemeSpec import space.kscience.dataforge.meta.descriptors.NodeDescriptor import space.kscience.dataforge.meta.descriptors.NodeDescriptorBuilder -import space.kscience.dataforge.meta.descriptors.ValueDescriptor +import space.kscience.dataforge.meta.descriptors.ValueDescriptorBuilder import space.kscience.dataforge.meta.toConfig import space.kscience.dataforge.values.ValueType import kotlin.reflect.KProperty1 @@ -15,7 +15,7 @@ import kotlin.reflect.typeOf */ public inline fun NodeDescriptorBuilder.value( property: KProperty1, - noinline block: ValueDescriptor.() -> Unit = {}, + noinline block: ValueDescriptorBuilder.() -> Unit = {}, ) { when (typeOf()) { typeOf(), typeOf(), typeOf(), typeOf(), typeOf(), typeOf() -> @@ -54,7 +54,7 @@ public fun NodeDescriptor.copy(block: NodeDescriptorBuilder.() -> Unit = {}): No public inline fun NodeDescriptorBuilder.scheme( property: KProperty1, spec: SchemeSpec, - noinline block: NodeDescriptor.() -> Unit = {}, + noinline block: NodeDescriptorBuilder.() -> Unit = {}, ) { spec.descriptor?.let { descriptor -> item(property.name, descriptor.copy(block)) diff --git a/visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/html/HtmlTagTest.kt b/visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/html/HtmlTagTest.kt index 5f35bd74..e50c13b3 100644 --- a/visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/html/HtmlTagTest.kt +++ b/visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/html/HtmlTagTest.kt @@ -2,13 +2,35 @@ package space.kscience.visionforge.html import kotlinx.html.* import kotlinx.html.stream.createHTML +import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.set import space.kscience.dataforge.misc.DFExperimental +import space.kscience.dataforge.names.Name +import space.kscience.visionforge.Vision import space.kscience.visionforge.VisionBase import space.kscience.visionforge.configure import space.kscience.visionforge.meta import kotlin.test.Test +typealias HtmlVisionRenderer = FlowContent.(name: Name, vision: Vision, meta: Meta) -> Unit + +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) { + visionMap[name] = vision + renderer(name, vision, outputMeta) + } + } + fragment(consumer) + return visionMap +} + + @DFExperimental class HtmlTagTest { @@ -46,15 +68,11 @@ class HtmlTagTest { } } - val groupRenderer: HtmlVisionRenderer = { _, _, _ -> - p { +"This is group" } - } - @Test fun testStringRender() { println( - createHTML().div { + createHTML().div{ renderVisionFragment(simpleVisionRenderer, fragment = fragment) } ) 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 47fed77b..8cd5dd09 100644 --- a/visionforge-core/src/jsMain/kotlin/space/kscience/visionforge/VisionClient.kt +++ b/visionforge-core/src/jsMain/kotlin/space/kscience/visionforge/VisionClient.kt @@ -7,6 +7,7 @@ import org.w3c.dom.url.URL import space.kscience.dataforge.context.* import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.MetaSerializer +import space.kscience.visionforge.html.RENDER_FUNCTION_NAME import space.kscience.visionforge.html.VisionTagConsumer import space.kscience.visionforge.html.VisionTagConsumer.Companion.OUTPUT_CONNECT_ATTRIBUTE import space.kscience.visionforge.html.VisionTagConsumer.Companion.OUTPUT_ENDPOINT_ATTRIBUTE @@ -221,7 +222,7 @@ public fun runVisionClient(contextBuilder: ContextBuilder.() -> Unit) { //plugin(VisionClient) } val visionClient = context.fetch(VisionClient) - window.asDynamic()["renderAllVisionsById"] = visionClient::renderAllVisionsById + window.asDynamic()[RENDER_FUNCTION_NAME] = visionClient::renderAllVisionsById visionClient.renderAllVisions() } \ No newline at end of file diff --git a/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/GdmlTransformer.kt b/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/GdmlTransformerEnv.kt similarity index 84% rename from visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/GdmlTransformer.kt rename to visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/GdmlTransformerEnv.kt index ac0e9e4b..c4a2364a 100644 --- a/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/GdmlTransformer.kt +++ b/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/GdmlTransformerEnv.kt @@ -25,8 +25,7 @@ private inline operator fun Number.times(d: Double) = toDouble() * d @Suppress("NOTHING_TO_INLINE") private inline operator fun Number.times(f: Float) = toFloat() * f -public class GdmlTransformerSettings { - public val random: Random = Random(222) +public class GdmlTransformer { public enum class Action { ADD, @@ -40,12 +39,70 @@ public class GdmlTransformerSettings { public var solidAction: (GdmlSolid) -> Action = { Action.PROTOTYPE } public var volumeAction: (GdmlGroup) -> Action = { Action.PROTOTYPE } - public var paint: SolidMaterial.(material: GdmlMaterial) -> Unit = { _ -> - color(random.nextInt(16777216)) + internal val styleCache = HashMap() + + public fun Solid.useStyle(name: String, builder: MetaBuilder.() -> Unit) { + styleCache.getOrPut(name.toName()) { + Meta(builder) + } + useStyle(name) + } + + public fun Solid.opaque() { + useStyle("opaque") { + SolidMaterial.MATERIAL_OPACITY_KEY put 0.3 + "edges.enabled" put true + } + } + + /** + * Configure paint for given solid with given [GdmlMaterial] + */ + public var configurePaint: SolidMaterial.(material: GdmlMaterial, solid: GdmlSolid) -> Unit = + { material, _ -> color(randomColor(material)) } + private set + + public fun paint(block: SolidMaterial.(material: GdmlMaterial, solid: GdmlSolid) -> Unit) { + configurePaint = block + } + + /** + * Configure given solid + */ + public var configureSolid: Solid.(parent: GdmlVolume, solid: GdmlSolid, material: GdmlMaterial) -> Unit = + { parent, solid, material -> + val styleName = "materials.${material.name}" + + if (parent.physVolumes.isNotEmpty()) opaque() + + useStyle(styleName) { + val vfMaterial = SolidMaterial().apply { + configurePaint(material, solid) + } + MATERIAL_KEY put vfMaterial.toMeta() + "Gdml.material" put material.name + } + } + private set + + + + public companion object { + private val random: Random = Random(222) + + private val colorCache = HashMap() + + /** + * Use random color and cache it based on the material. Meaning that colors are random, but always the same for the + * same material. + */ + public fun randomColor(material: GdmlMaterial): Int { + return colorCache.getOrPut(material) { random.nextInt(16777216) } + } } } -private class GdmlTransformer(val settings: GdmlTransformerSettings) { +private class GdmlTransformerEnv(val settings: GdmlTransformer) { //private val materialCache = HashMap() /** @@ -57,9 +114,13 @@ private class GdmlTransformer(val settings: GdmlTransformerSettings) { setProperty("edges.enabled", false) } - private val referenceStore = HashMap>() + fun Solid.configureSolid(root: Gdml, parent: GdmlVolume, solid: GdmlSolid) { + val material = parent.materialref.resolve(root) ?: GdmlElement(parent.materialref.ref) + settings.run { configureSolid(parent, solid, material) } + } + private fun proxySolid(root: Gdml, group: SolidGroup, solid: GdmlSolid, name: String): SolidReferenceGroup { val templateName = solidsName + name if (proto[templateName] == null) { @@ -85,38 +146,6 @@ private class GdmlTransformer(val settings: GdmlTransformerSettings) { return ref } - private val styleCache = HashMap() - - var solidConfiguration: Solid.(parent: GdmlVolume, solid: GdmlSolid) -> Unit = { parent, _ -> - if (parent.physVolumes.isNotEmpty()) { - useStyle("opaque") { - SolidMaterial.MATERIAL_OPACITY_KEY put 0.3 - "edges.enabled" put true - } - } - } - - fun Solid.useStyle(name: String, builder: MetaBuilder.() -> Unit) { - styleCache.getOrPut(name.toName()) { - Meta(builder) - } - useStyle(name) - } - - fun configureSolid(root: Gdml, obj: Solid, parent: GdmlVolume, solid: GdmlSolid) { - val material = parent.materialref.resolve(root) ?: GdmlElement(parent.materialref.ref) - - val styleName = "materials.${material.name}" - - obj.useStyle(styleName) { - val vfMaterial = settings.run { SolidMaterial().apply { paint(material) } } - MATERIAL_KEY put vfMaterial.toMeta() - "Gdml.material" put material.name - } - - obj.solidConfiguration(parent, solid) - } - fun T.withPosition( newPos: GdmlPosition? = null, newRotation: GdmlRotation? = null, @@ -314,13 +343,13 @@ private class GdmlTransformer(val settings: GdmlTransformerSettings) { ): Solid? { require(name != "") { "Can't use empty solid name. Use null instead." } return when (settings.solidAction(solid)) { - GdmlTransformerSettings.Action.ADD -> { + GdmlTransformer.Action.ADD -> { addSolid(root, solid, name) } - GdmlTransformerSettings.Action.PROTOTYPE -> { + GdmlTransformer.Action.PROTOTYPE -> { proxySolid(root, this, solid, name ?: solid.name) } - GdmlTransformerSettings.Action.REJECT -> { + GdmlTransformer.Action.REJECT -> { //ignore null } @@ -339,21 +368,21 @@ private class GdmlTransformer(val settings: GdmlTransformerSettings) { val solid = volume.solidref.resolve(root) ?: error("Solid with tag ${volume.solidref.ref} for volume ${volume.name} not defined") addSolidWithCaching(root, solid, physVolume.name)?.apply { - configureSolid(root, this, volume, solid) + configureSolid(root, volume, solid) withPosition(root, physVolume) } return } when (settings.volumeAction(volume)) { - GdmlTransformerSettings.Action.ADD -> { + GdmlTransformer.Action.ADD -> { val group: SolidGroup = volume(root, volume) this[physVolume.name] = group.withPosition(root, physVolume) } - GdmlTransformerSettings.Action.PROTOTYPE -> { + GdmlTransformer.Action.PROTOTYPE -> { proxyVolume(root, this, physVolume, volume) } - GdmlTransformerSettings.Action.REJECT -> { + GdmlTransformer.Action.REJECT -> { //ignore } } @@ -379,7 +408,7 @@ private class GdmlTransformer(val settings: GdmlTransformerSettings) { ?: error("Solid with tag ${group.solidref.ref} for volume ${group.name} not defined") addSolidWithCaching(root, solid, null)?.apply { - configureSolid(root, this, group, solid) + this.configureSolid(root, group, solid) } when (val vol: GdmlPlacement? = group.placement) { @@ -394,10 +423,10 @@ private class GdmlTransformer(val settings: GdmlTransformerSettings) { } private fun finalize(final: SolidGroup): SolidGroup { - //final.prototypes = proto - final.useStyle("gdml") { + val rootStyle by final.style("gdml") { Solid.ROTATION_ORDER_KEY put RotationOrder.ZXY } + final.useStyle(rootStyle) //inline prototypes // referenceStore.forEach { (protoName, list) -> @@ -419,7 +448,7 @@ private class GdmlTransformer(val settings: GdmlTransformerSettings) { set(token.asName(), item as? Solid) } } - styleCache.forEach { + settings.styleCache.forEach { final.styleSheet { define(it.key.toString(), it.value) } @@ -432,15 +461,16 @@ private class GdmlTransformer(val settings: GdmlTransformerSettings) { } -public fun Gdml.toVision(block: GdmlTransformerSettings.() -> Unit = {}): SolidGroup { - val context = GdmlTransformer(GdmlTransformerSettings().apply(block)) +public fun Gdml.toVision(block: GdmlTransformer.() -> Unit = {}): SolidGroup { + val settings = GdmlTransformer().apply(block) + val context = GdmlTransformerEnv(settings) return context.transform(this) } /** * Append Gdml node to the group */ -public fun SolidGroup.gdml(gdml: Gdml, key: String? = null, transformer: GdmlTransformerSettings.() -> Unit = {}) { +public fun SolidGroup.gdml(gdml: Gdml, key: String? = null, transformer: GdmlTransformer.() -> Unit = {}) { val visual = gdml.toVision(transformer) //println(Visual3DPlugin.json.stringify(VisualGroup3D.serializer(), visual)) set(key, visual) diff --git a/visionforge-gdml/src/jvmMain/kotlin/space/kscience/visionforge/gdml/gdmlJVM.kt b/visionforge-gdml/src/jvmMain/kotlin/space/kscience/visionforge/gdml/gdmlJVM.kt index 310631ce..7bd17a5c 100644 --- a/visionforge-gdml/src/jvmMain/kotlin/space/kscience/visionforge/gdml/gdmlJVM.kt +++ b/visionforge-gdml/src/jvmMain/kotlin/space/kscience/visionforge/gdml/gdmlJVM.kt @@ -12,7 +12,7 @@ public fun SolidGroup.gdml( file: Path, key: String = "", usePreprocessor: Boolean = false, - transformer: GdmlTransformerSettings.() -> Unit = {}, + transformer: GdmlTransformer.() -> Unit = {}, ) { val gdml = Gdml.decodeFromFile(file, usePreprocessor) gdml(gdml, key, transformer) diff --git a/visionforge-server/src/main/kotlin/space/kscience/visionforge/three/server/VisionServer.kt b/visionforge-server/src/main/kotlin/space/kscience/visionforge/three/server/VisionServer.kt index c52a84a1..06c13bfe 100644 --- a/visionforge-server/src/main/kotlin/space/kscience/visionforge/three/server/VisionServer.kt +++ b/visionforge-server/src/main/kotlin/space/kscience/visionforge/three/server/VisionServer.kt @@ -89,7 +89,7 @@ public class VisionServer internal constructor( script { attributes["class"] = OUTPUT_DATA_CLASS unsafe { - +visionManager.encodeToString(vision) + +"\n${visionManager.encodeToString(vision)}\n" } } } diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Axes.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Axes.kt index 8b59a8b4..2f3e1645 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Axes.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Axes.kt @@ -18,9 +18,15 @@ public class Axes : Scheme() { override val descriptor: NodeDescriptor by lazy { NodeDescriptor { - value(Axes::visible) - value(Axes::size) - value(Axes::width) + value(Axes::visible){ + default(false) + } + value(Axes::size){ + default(AXIS_SIZE) + } + value(Axes::width){ + default(AXIS_WIDTH) + } } } } diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Camera.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Camera.kt index 00c36d50..b6209b54 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Camera.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Camera.kt @@ -30,12 +30,24 @@ public class Camera : Scheme() { override val descriptor: NodeDescriptor by lazy { NodeDescriptor { - value(Camera::fov) - value(Camera::nearClip) - value(Camera::farClip) - value(Camera::distance) - value(Camera::azimuth) - value(Camera::zenith) + value(Camera::fov){ + default(FIELD_OF_VIEW) + } + value(Camera::nearClip){ + default(NEAR_CLIP) + } + value(Camera::farClip){ + default(FAR_CLIP) + } + value(Camera::distance){ + default(INITIAL_DISTANCE) + } + value(Camera::azimuth){ + default(INITIAL_AZIMUTH) + } + value(Camera::latitude){ + default(INITIAL_LATITUDE) + } } } } diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Canvas3DOptions.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Canvas3DOptions.kt index bd3eebc3..aa7cb258 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Canvas3DOptions.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Canvas3DOptions.kt @@ -3,8 +3,11 @@ package space.kscience.visionforge.solid.specifications import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.descriptors.NodeDescriptor import space.kscience.dataforge.names.Name +import space.kscience.dataforge.values.ValueType +import space.kscience.visionforge.hide import space.kscience.visionforge.scheme import space.kscience.visionforge.value +import space.kscience.visionforge.widgetType public class Canvas3DOptions : Scheme() { public var axes: Axes by spec(Axes) @@ -20,6 +23,8 @@ public class Canvas3DOptions : Scheme() { public var maxWith: Number by number { maxSize } public var maxHeight: Number by number { maxSize } + public var layers: List by numberList(0) + public var onSelect: ((Name?) -> Unit)? = null @@ -28,14 +33,37 @@ public class Canvas3DOptions : Scheme() { NodeDescriptor { scheme(Canvas3DOptions::axes, Axes) scheme(Canvas3DOptions::light, Light) - scheme(Canvas3DOptions::camera, Camera) - scheme(Canvas3DOptions::controls, Controls) - value(Canvas3DOptions::minSize) - value(Canvas3DOptions::minWith) - value(Canvas3DOptions::minHeight) - value(Canvas3DOptions::maxSize) - value(Canvas3DOptions::maxWith) - value(Canvas3DOptions::maxHeight) + scheme(Canvas3DOptions::camera, Camera) { + hide() + } + scheme(Canvas3DOptions::controls, Controls) { + hide() + } + value(Canvas3DOptions::minSize) { + hide() + } + value(Canvas3DOptions::minWith) { + hide() + } + value(Canvas3DOptions::minHeight) { + hide() + } + value(Canvas3DOptions::maxSize) { + hide() + } + value(Canvas3DOptions::maxWith) { + hide() + } + value(Canvas3DOptions::maxHeight) { + hide() + } + value(Canvas3DOptions::layers) { + type(ValueType.NUMBER) + multiple = true + default(listOf(0)) + widgetType = "multiSelect" + allow(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + } } } } 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 034cf430..8d0e0d0d 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 @@ -52,7 +52,7 @@ public class ThreeCanvas( private set private val scene: Scene = Scene().apply { - options.useProperty(Canvas3DOptions::axes){axesConfig-> + options.useProperty(Canvas3DOptions::axes) { axesConfig -> getObjectByName(AXES_NAME)?.let { remove(it) } val axesObject = AxesHelper(axes.size.toInt()).apply { visible = axes.visible } axesObject.name = AXES_NAME @@ -60,7 +60,7 @@ public class ThreeCanvas( } //Set up light - options.useProperty(Canvas3DOptions::light){lightConfig-> + options.useProperty(Canvas3DOptions::light) { lightConfig -> //remove old light if present getObjectByName(LIGHT_NAME)?.let { remove(it) } //add new light @@ -70,8 +70,29 @@ public class ThreeCanvas( } } - public var camera: PerspectiveCamera = buildCamera(options.camera) - private set + + private fun buildCamera(spec: Camera) = PerspectiveCamera( + spec.fov, + 1.0, + spec.nearClip, + spec.farClip + ).apply { + translateX(spec.distance * sin(spec.zenith) * sin(spec.azimuth)) + translateY(spec.distance * cos(spec.zenith)) + translateZ(spec.distance * sin(spec.zenith) * cos(spec.azimuth)) + options.useProperty(Canvas3DOptions::layers) { selectedLayers -> + (0..31).forEach { + if (it in selectedLayers) { + this@apply.layers.enable(it) + } else{ + this@apply.layers.disable(it) + } + } + } + } + + + public val camera: PerspectiveCamera = buildCamera(options.camera) private var picked: Object3D? = null @@ -127,7 +148,6 @@ public class ThreeCanvas( mousePosition.x = ((event.clientX - rect.left) / canvas.clientWidth) * 2 - 1 mousePosition.y = -((event.clientY - rect.top) / canvas.clientHeight) * 2 + 1 } - event.preventDefault() }, false) canvas.onresize = { @@ -185,17 +205,6 @@ public class ThreeCanvas( private fun buildLight(spec: Light?): info.laht.threekt.lights.Light = AmbientLight(0x404040) - private fun buildCamera(spec: Camera) = PerspectiveCamera( - spec.fov, - 1.0, - spec.nearClip, - spec.farClip - ).apply { - translateX(spec.distance * sin(spec.zenith) * sin(spec.azimuth)) - translateY(spec.distance * cos(spec.zenith)) - translateZ(spec.distance * sin(spec.zenith) * cos(spec.azimuth)) - } - private fun addControls(element: Node, controls: Controls) { when (controls["type"].string) { "trackball" -> TrackballControls(camera, element)