diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/Colors.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/Colors.kt index 0365e300..437c30ab 100644 --- a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/Colors.kt +++ b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/Colors.kt @@ -174,4 +174,9 @@ object Colors { const val whitesmoke = 0xF5F5F5 const val yellow = 0xFFFF00 const val yellowgreen = 0x9ACD32 + + fun rgbToString(rgb: Int): String { + val string = rgb.toString(16) + return "#" + string.substring(string.length - 6) + } } \ No newline at end of file diff --git a/dataforge-vis-spatial-gdml/src/commonMain/kotlin/hep/dataforge/vis/spatial/gdml/GDMLTransformer.kt b/dataforge-vis-spatial-gdml/src/commonMain/kotlin/hep/dataforge/vis/spatial/gdml/GDMLTransformer.kt index ad8ac85d..85d8ddec 100644 --- a/dataforge-vis-spatial-gdml/src/commonMain/kotlin/hep/dataforge/vis/spatial/gdml/GDMLTransformer.kt +++ b/dataforge-vis-spatial-gdml/src/commonMain/kotlin/hep/dataforge/vis/spatial/gdml/GDMLTransformer.kt @@ -2,6 +2,8 @@ package hep.dataforge.vis.spatial.gdml import hep.dataforge.meta.Meta import hep.dataforge.meta.buildMeta +import hep.dataforge.names.toName +import hep.dataforge.vis.common.Colors import hep.dataforge.vis.spatial.VisualGroup3D import hep.dataforge.vis.spatial.VisualObject3D import hep.dataforge.vis.spatial.material @@ -36,10 +38,10 @@ class GDMLTransformer(val root: GDML) { val materialColor = materialCache.getOrPut(material) { buildMeta { - "color" to random.nextInt(0, Int.MAX_VALUE) + "color" to Colors.rgbToString(random.nextInt(0, Int.MAX_VALUE)) } } - + obj.setProperty("gdml.material".toName(), material.name) obj.material = materialColor obj.solidConfiguration(parent, solid) } diff --git a/dataforge-vis-spatial-gdml/src/jsMain/kotlin/hep/dataforge/vis/spatial/gdml/demo/GDMLDemoApp.kt b/dataforge-vis-spatial-gdml/src/jsMain/kotlin/hep/dataforge/vis/spatial/gdml/demo/GDMLDemoApp.kt index 79465f82..b55529fc 100644 --- a/dataforge-vis-spatial-gdml/src/jsMain/kotlin/hep/dataforge/vis/spatial/gdml/demo/GDMLDemoApp.kt +++ b/dataforge-vis-spatial-gdml/src/jsMain/kotlin/hep/dataforge/vis/spatial/gdml/demo/GDMLDemoApp.kt @@ -1,10 +1,7 @@ package hep.dataforge.vis.spatial.gdml.demo import hep.dataforge.context.Global -import hep.dataforge.meta.get -import hep.dataforge.meta.string import hep.dataforge.vis.common.VisualGroup -import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.hmr.ApplicationBase import hep.dataforge.vis.hmr.startApplication import hep.dataforge.vis.spatial.* @@ -14,11 +11,15 @@ import hep.dataforge.vis.spatial.gdml.toVisual import hep.dataforge.vis.spatial.three.ThreeOutput import hep.dataforge.vis.spatial.three.ThreePlugin import hep.dataforge.vis.spatial.three.output +import hep.dataforge.vis.spatial.tree.propertyEditor import hep.dataforge.vis.spatial.tree.render import hep.dataforge.vis.spatial.tree.toTree import kotlinx.html.InputType import kotlinx.html.dom.append -import kotlinx.html.js.* +import kotlinx.html.js.input +import kotlinx.html.js.li +import kotlinx.html.js.p +import kotlinx.html.js.ul import org.w3c.dom.Element import org.w3c.dom.HTMLDivElement import org.w3c.dom.HTMLElement @@ -150,43 +151,6 @@ private class GDMLDemoApp : ApplicationBase() { } - fun showEditor(item: VisualObject?, name: String?) { - val element = document.getElementById("editor") ?: error("Element with id 'canvas' not found on page") - element.clear() - if (item != null) { - element.append { - div("card") { - div("card-body") { - h3(classes = "card-title") { +(name ?: "") } - form { - div("form-group") { - label { - +"Color: " - } - input(InputType.color, classes = "form-control").apply { - onchange = { event -> - item.color(value) - } - } - } - div("form-group form-check") { - input(InputType.checkBox, classes = "form-check-input").apply { - this.value = item.material?.get("color").string ?: "" - onchange = { event -> - item.visible = checked - Unit - } - } - label("form-check-label") { +"Visible" } - } - } - } - } - } - } - } - - override fun start(state: Map) { val context = Global.context("demo") {} @@ -196,6 +160,7 @@ private class GDMLDemoApp : ApplicationBase() { val canvas = document.getElementById("canvas") ?: error("Element with id 'canvas' not found on page") val layers = document.getElementById("layers") ?: error("Element with id 'layers' not found on page") val tree = document.getElementById("tree") ?: error("Element with id 'tree' not found on page") + val editor = document.getElementById("editor") ?: error("Element with id 'editor' not found on page") canvas.clear() val action: (name: String, data: String) -> Unit = { name, data -> @@ -224,7 +189,7 @@ private class GDMLDemoApp : ApplicationBase() { setupLayers(layers, output) if (visual is VisualGroup) { - visual.toTree(::showEditor).render(tree as HTMLElement) { + visual.toTree(editor::propertyEditor).render(tree as HTMLElement) { showCheckboxes = true } } diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Proxy.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Proxy.kt index 3c9ca492..44bc6b82 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Proxy.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Proxy.kt @@ -85,11 +85,7 @@ class Proxy(val templateName: Name) : AbstractVisualObject(), VisualGroup, Visua inner class ProxyChild(val name: Name) : AbstractVisualObject() { override var properties: Config? - get() = propertyCache.getOrPut(name) { - Config().apply { - attachListener(this@ProxyChild) - } - } + get() = propertyCache[name] set(value) { if (value == null) { propertyCache.remove(name)?.removeListener(this@Proxy) @@ -99,7 +95,6 @@ class Proxy(val templateName: Name) : AbstractVisualObject(), VisualGroup, Visua } } } - } } diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/VisualObject3D.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/VisualObject3D.kt index 79804d44..cb882f18 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/VisualObject3D.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/VisualObject3D.kt @@ -6,6 +6,7 @@ import hep.dataforge.io.NameSerializer import hep.dataforge.meta.* import hep.dataforge.names.plus import hep.dataforge.output.Output +import hep.dataforge.vis.common.Colors.rgbToString import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.common.asName import hep.dataforge.vis.spatial.VisualObject3D.Companion.DETAIL_KEY @@ -124,13 +125,21 @@ var VisualObject.selected: Boolean? set(value) = setProperty(SELECTED_KEY, value) fun VisualObject.color(rgb: Int) { - material = (material?.builder() ?: MetaBuilder()).apply { "color" to rgb } + material = (material?.builder() ?: MetaBuilder()).apply { "color" to rgbToString(rgb) } } fun VisualObject.color(rgb: String) { material = (material?.builder() ?: MetaBuilder()).apply { "color" to rgb } } +var VisualObject.color: String? + get() = material["color"].string + set(value) { + if (value != null) { + color(value) + } + } + fun VisualObject3D.material(builder: MetaBuilder.() -> Unit) { material = buildMeta(builder) } diff --git a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeOutput.kt b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeOutput.kt index 2e8bd0e4..91438022 100644 --- a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeOutput.kt +++ b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeOutput.kt @@ -32,15 +32,14 @@ class ThreeOutput(val three: ThreePlugin, val meta: Meta = EmptyMeta) : Output Int = { this.offsetWidth }) { + fun attach(element: HTMLElement) { element.clear() - val width by meta.number(computeWidth(element)).int - val height: Int = (width / camera.aspect).toInt() + camera.aspect = 1.0 val renderer = WebGLRenderer { antialias = true }.apply { setClearColor(Colors.skyblue, 1) - setSize(width, height) + } three.addControls(camera, renderer.domElement, meta["controls"].node ?: EmptyMeta) @@ -52,13 +51,19 @@ class ThreeOutput(val three: ThreePlugin, val meta: Meta = EmptyMeta) : Output { } } - obj.onChildrenChange(object3D) { name, propertyHolder -> - (object3D.findChild(name) as? Mesh)?.updateProperties(propertyHolder) + obj.onChildrenChange(this) { name, propertyHolder -> + val child = object3D.findChild(name) + (child as? Mesh)?.updateProperties(propertyHolder) } return object3D diff --git a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/tree/jsVisualTree.kt b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/tree/jsVisualTree.kt index 6f9f22ea..191e06ab 100644 --- a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/tree/jsVisualTree.kt +++ b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/tree/jsVisualTree.kt @@ -9,6 +9,7 @@ import hep.dataforge.names.NameToken import hep.dataforge.vis.common.VisualGroup import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.common.getProperty +import hep.dataforge.vis.spatial.Proxy import hep.dataforge.vis.spatial.selected import hep.dataforge.vis.spatial.visible import org.w3c.dom.HTMLElement @@ -32,13 +33,24 @@ internal fun createInspireTree(block: Config.() -> Unit = {}): InspireTree { return InspireTree(config) } -fun VisualGroup.toTree(onFocus: (VisualObject?, String?)->Unit = {obj,name->}): InspireTree { +fun VisualGroup.toTree(onFocus: (VisualObject?, String?) -> Unit = { obj, name -> }): InspireTree { val map = HashMap() fun generateNodeConfig(item: VisualObject, fullName: Name): NodeConfig { val title = item.getProperty("title").string ?: fullName.last()?.toString() ?: "root" - val text = "$title[${item::class.toString().replace("class ","")}]" + val className = if (item is Proxy) { + item.template::class.toString() + } else { + item::class.toString() + }.replace("class ", "") + + val text: String = if (title.startsWith("@")) { + "[$className}]" + } else { + "$title[$className}]" + } + return json( "children" to if ((item as? VisualGroup)?.children?.isEmpty() != false) { emptyArray() @@ -68,7 +80,7 @@ fun VisualGroup.toTree(onFocus: (VisualObject?, String?)->Unit = {obj,name->}): } } - val inspireTree = createInspireTree{ + val inspireTree = createInspireTree { } val nodeConfig = generateNodeConfig(this, EmptyName) @@ -93,14 +105,14 @@ fun VisualGroup.toTree(onFocus: (VisualObject?, String?)->Unit = {obj,name->}): } inspireTree.on("node.unchecked") { node: TreeNode -> - if(!node.indeterminate()){ + if (!node.indeterminate()) { map[node.id]?.visible = node.checked() } } inspireTree.on("node.focused") { node: TreeNode, isLoadEvent: Boolean -> if (!isLoadEvent) { - onFocus(map[node.id],node.id) + onFocus(map[node.id], node.id) } } diff --git a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/tree/propertyEditor.kt b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/tree/propertyEditor.kt new file mode 100644 index 00000000..dc030b8d --- /dev/null +++ b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/tree/propertyEditor.kt @@ -0,0 +1,67 @@ +package hep.dataforge.vis.spatial.tree + +import hep.dataforge.vis.common.VisualObject +import hep.dataforge.vis.spatial.color +import hep.dataforge.vis.spatial.opacity +import hep.dataforge.vis.spatial.visible +import kotlinx.html.InputType +import kotlinx.html.dom.append +import kotlinx.html.js.* +import org.w3c.dom.Element +import kotlin.dom.clear + +fun Element.propertyEditor(item: VisualObject?, name: String?) { + clear() + if (item != null) { + append { + div("card") { + div("card-body") { + h3(classes = "card-title") { +(name ?: "") } + form { + div("form-group row") { + label("col-form-label col-4") { + +"Color: " + } + input(InputType.color, classes = "form-control col-8") { + value = item.color ?: "#ffffff" + }.apply { + onInputFunction = { + item.color = value + } + } + } + div("form-group row") { + label("col-form-label col-4") { + +"Opacity: " + } + input(InputType.range, classes = "form-control col-8") { + min = "0.0" + max = "1.0" + step = "0.1" + value = item.opacity.toString() + }.apply { + onInputFunction = { + item.opacity = value.toDouble() + } + } + } + div("form-group row") { + label("col-form-label col-4") { +"Visible: " } + div("col-8") { + div("form-check") { + input(InputType.checkBox, classes = "form-check-input").apply { + this.checked = item.visible ?: true + onInputFunction = { + item.visible = checked + } + } + } + } + + } + } + } + } + } + } +}