Tree editor tweaks
This commit is contained in:
parent
4fa2e50777
commit
c5f14fb5e7
@ -174,4 +174,9 @@ object Colors {
|
|||||||
const val whitesmoke = 0xF5F5F5
|
const val whitesmoke = 0xF5F5F5
|
||||||
const val yellow = 0xFFFF00
|
const val yellow = 0xFFFF00
|
||||||
const val yellowgreen = 0x9ACD32
|
const val yellowgreen = 0x9ACD32
|
||||||
|
|
||||||
|
fun rgbToString(rgb: Int): String {
|
||||||
|
val string = rgb.toString(16)
|
||||||
|
return "#" + string.substring(string.length - 6)
|
||||||
|
}
|
||||||
}
|
}
|
@ -2,6 +2,8 @@ package hep.dataforge.vis.spatial.gdml
|
|||||||
|
|
||||||
import hep.dataforge.meta.Meta
|
import hep.dataforge.meta.Meta
|
||||||
import hep.dataforge.meta.buildMeta
|
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.VisualGroup3D
|
||||||
import hep.dataforge.vis.spatial.VisualObject3D
|
import hep.dataforge.vis.spatial.VisualObject3D
|
||||||
import hep.dataforge.vis.spatial.material
|
import hep.dataforge.vis.spatial.material
|
||||||
@ -36,10 +38,10 @@ class GDMLTransformer(val root: GDML) {
|
|||||||
|
|
||||||
val materialColor = materialCache.getOrPut(material) {
|
val materialColor = materialCache.getOrPut(material) {
|
||||||
buildMeta {
|
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.material = materialColor
|
||||||
obj.solidConfiguration(parent, solid)
|
obj.solidConfiguration(parent, solid)
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
package hep.dataforge.vis.spatial.gdml.demo
|
package hep.dataforge.vis.spatial.gdml.demo
|
||||||
|
|
||||||
import hep.dataforge.context.Global
|
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.VisualGroup
|
||||||
import hep.dataforge.vis.common.VisualObject
|
|
||||||
import hep.dataforge.vis.hmr.ApplicationBase
|
import hep.dataforge.vis.hmr.ApplicationBase
|
||||||
import hep.dataforge.vis.hmr.startApplication
|
import hep.dataforge.vis.hmr.startApplication
|
||||||
import hep.dataforge.vis.spatial.*
|
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.ThreeOutput
|
||||||
import hep.dataforge.vis.spatial.three.ThreePlugin
|
import hep.dataforge.vis.spatial.three.ThreePlugin
|
||||||
import hep.dataforge.vis.spatial.three.output
|
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.render
|
||||||
import hep.dataforge.vis.spatial.tree.toTree
|
import hep.dataforge.vis.spatial.tree.toTree
|
||||||
import kotlinx.html.InputType
|
import kotlinx.html.InputType
|
||||||
import kotlinx.html.dom.append
|
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.Element
|
||||||
import org.w3c.dom.HTMLDivElement
|
import org.w3c.dom.HTMLDivElement
|
||||||
import org.w3c.dom.HTMLElement
|
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<String, Any>) {
|
override fun start(state: Map<String, Any>) {
|
||||||
|
|
||||||
val context = Global.context("demo") {}
|
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 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 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 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()
|
canvas.clear()
|
||||||
|
|
||||||
val action: (name: String, data: String) -> Unit = { name, data ->
|
val action: (name: String, data: String) -> Unit = { name, data ->
|
||||||
@ -224,7 +189,7 @@ private class GDMLDemoApp : ApplicationBase() {
|
|||||||
setupLayers(layers, output)
|
setupLayers(layers, output)
|
||||||
|
|
||||||
if (visual is VisualGroup) {
|
if (visual is VisualGroup) {
|
||||||
visual.toTree(::showEditor).render(tree as HTMLElement) {
|
visual.toTree(editor::propertyEditor).render(tree as HTMLElement) {
|
||||||
showCheckboxes = true
|
showCheckboxes = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -85,11 +85,7 @@ class Proxy(val templateName: Name) : AbstractVisualObject(), VisualGroup, Visua
|
|||||||
|
|
||||||
inner class ProxyChild(val name: Name) : AbstractVisualObject() {
|
inner class ProxyChild(val name: Name) : AbstractVisualObject() {
|
||||||
override var properties: Config?
|
override var properties: Config?
|
||||||
get() = propertyCache.getOrPut(name) {
|
get() = propertyCache[name]
|
||||||
Config().apply {
|
|
||||||
attachListener(this@ProxyChild)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
set(value) {
|
set(value) {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
propertyCache.remove(name)?.removeListener(this@Proxy)
|
propertyCache.remove(name)?.removeListener(this@Proxy)
|
||||||
@ -99,7 +95,6 @@ class Proxy(val templateName: Name) : AbstractVisualObject(), VisualGroup, Visua
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import hep.dataforge.io.NameSerializer
|
|||||||
import hep.dataforge.meta.*
|
import hep.dataforge.meta.*
|
||||||
import hep.dataforge.names.plus
|
import hep.dataforge.names.plus
|
||||||
import hep.dataforge.output.Output
|
import hep.dataforge.output.Output
|
||||||
|
import hep.dataforge.vis.common.Colors.rgbToString
|
||||||
import hep.dataforge.vis.common.VisualObject
|
import hep.dataforge.vis.common.VisualObject
|
||||||
import hep.dataforge.vis.common.asName
|
import hep.dataforge.vis.common.asName
|
||||||
import hep.dataforge.vis.spatial.VisualObject3D.Companion.DETAIL_KEY
|
import hep.dataforge.vis.spatial.VisualObject3D.Companion.DETAIL_KEY
|
||||||
@ -124,13 +125,21 @@ var VisualObject.selected: Boolean?
|
|||||||
set(value) = setProperty(SELECTED_KEY, value)
|
set(value) = setProperty(SELECTED_KEY, value)
|
||||||
|
|
||||||
fun VisualObject.color(rgb: Int) {
|
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) {
|
fun VisualObject.color(rgb: String) {
|
||||||
material = (material?.builder() ?: MetaBuilder()).apply { "color" to rgb }
|
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) {
|
fun VisualObject3D.material(builder: MetaBuilder.() -> Unit) {
|
||||||
material = buildMeta(builder)
|
material = buildMeta(builder)
|
||||||
}
|
}
|
||||||
|
@ -32,15 +32,14 @@ class ThreeOutput(val three: ThreePlugin, val meta: Meta = EmptyMeta) : Output<V
|
|||||||
|
|
||||||
val camera = three.buildCamera(meta["camera"].node ?: EmptyMeta)
|
val camera = three.buildCamera(meta["camera"].node ?: EmptyMeta)
|
||||||
|
|
||||||
fun attach(element: HTMLElement, computeWidth: HTMLElement.() -> Int = { this.offsetWidth }) {
|
fun attach(element: HTMLElement) {
|
||||||
element.clear()
|
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 {
|
val renderer = WebGLRenderer { antialias = true }.apply {
|
||||||
setClearColor(Colors.skyblue, 1)
|
setClearColor(Colors.skyblue, 1)
|
||||||
setSize(width, height)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
three.addControls(camera, renderer.domElement, meta["controls"].node ?: EmptyMeta)
|
three.addControls(camera, renderer.domElement, meta["controls"].node ?: EmptyMeta)
|
||||||
@ -52,13 +51,19 @@ class ThreeOutput(val three: ThreePlugin, val meta: Meta = EmptyMeta) : Output<V
|
|||||||
renderer.render(scene, camera)
|
renderer.render(scene, camera)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
element.appendChild(renderer.domElement)
|
||||||
|
|
||||||
elementResizeEvent(element) {
|
elementResizeEvent(element) {
|
||||||
|
renderer.setSize(element.offsetWidth, element.offsetWidth)
|
||||||
camera.updateProjectionMatrix()
|
camera.updateProjectionMatrix()
|
||||||
val newWidth = computeWidth(element)
|
|
||||||
renderer.setSize(newWidth, (newWidth / camera.aspect).toInt())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
element.appendChild(renderer.domElement)
|
// val width by meta.number(element.offsetWidth).int
|
||||||
|
// val height by meta.number(element.offsetHeight).int
|
||||||
|
// val size = min(width, height)
|
||||||
|
// renderer.setSize(size, size)
|
||||||
|
renderer.setSize(element.offsetWidth, element.offsetWidth)
|
||||||
|
|
||||||
animate()
|
animate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,8 +45,9 @@ class ThreeProxyFactory(val three: ThreePlugin) : ThreeFactory<Proxy> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
obj.onChildrenChange(object3D) { name, propertyHolder ->
|
obj.onChildrenChange(this) { name, propertyHolder ->
|
||||||
(object3D.findChild(name) as? Mesh)?.updateProperties(propertyHolder)
|
val child = object3D.findChild(name)
|
||||||
|
(child as? Mesh)?.updateProperties(propertyHolder)
|
||||||
}
|
}
|
||||||
|
|
||||||
return object3D
|
return object3D
|
||||||
|
@ -9,6 +9,7 @@ import hep.dataforge.names.NameToken
|
|||||||
import hep.dataforge.vis.common.VisualGroup
|
import hep.dataforge.vis.common.VisualGroup
|
||||||
import hep.dataforge.vis.common.VisualObject
|
import hep.dataforge.vis.common.VisualObject
|
||||||
import hep.dataforge.vis.common.getProperty
|
import hep.dataforge.vis.common.getProperty
|
||||||
|
import hep.dataforge.vis.spatial.Proxy
|
||||||
import hep.dataforge.vis.spatial.selected
|
import hep.dataforge.vis.spatial.selected
|
||||||
import hep.dataforge.vis.spatial.visible
|
import hep.dataforge.vis.spatial.visible
|
||||||
import org.w3c.dom.HTMLElement
|
import org.w3c.dom.HTMLElement
|
||||||
@ -32,13 +33,24 @@ internal fun createInspireTree(block: Config.() -> Unit = {}): InspireTree {
|
|||||||
return InspireTree(config)
|
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<String, VisualObject>()
|
val map = HashMap<String, VisualObject>()
|
||||||
|
|
||||||
fun generateNodeConfig(item: VisualObject, fullName: Name): NodeConfig {
|
fun generateNodeConfig(item: VisualObject, fullName: Name): NodeConfig {
|
||||||
val title = item.getProperty("title").string ?: fullName.last()?.toString() ?: "root"
|
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(
|
return json(
|
||||||
"children" to if ((item as? VisualGroup)?.children?.isEmpty() != false) {
|
"children" to if ((item as? VisualGroup)?.children?.isEmpty() != false) {
|
||||||
emptyArray<NodeConfig>()
|
emptyArray<NodeConfig>()
|
||||||
@ -68,7 +80,7 @@ fun VisualGroup.toTree(onFocus: (VisualObject?, String?)->Unit = {obj,name->}):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val inspireTree = createInspireTree{
|
val inspireTree = createInspireTree {
|
||||||
|
|
||||||
}
|
}
|
||||||
val nodeConfig = generateNodeConfig(this, EmptyName)
|
val nodeConfig = generateNodeConfig(this, EmptyName)
|
||||||
@ -93,14 +105,14 @@ fun VisualGroup.toTree(onFocus: (VisualObject?, String?)->Unit = {obj,name->}):
|
|||||||
}
|
}
|
||||||
|
|
||||||
inspireTree.on("node.unchecked") { node: TreeNode ->
|
inspireTree.on("node.unchecked") { node: TreeNode ->
|
||||||
if(!node.indeterminate()){
|
if (!node.indeterminate()) {
|
||||||
map[node.id]?.visible = node.checked()
|
map[node.id]?.visible = node.checked()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inspireTree.on("node.focused") { node: TreeNode, isLoadEvent: Boolean ->
|
inspireTree.on("node.focused") { node: TreeNode, isLoadEvent: Boolean ->
|
||||||
if (!isLoadEvent) {
|
if (!isLoadEvent) {
|
||||||
onFocus(map[node.id],node.id)
|
onFocus(map[node.id], node.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user