Tree editor tweaks

This commit is contained in:
Alexander Nozik 2019-09-29 22:11:09 +03:00
parent 4fa2e50777
commit c5f14fb5e7
9 changed files with 126 additions and 65 deletions

View File

@ -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)
}
}

View File

@ -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)
}

View File

@ -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<String, Any>) {
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
}
}

View File

@ -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
}
}
}
}
}

View File

@ -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)
}

View File

@ -32,15 +32,14 @@ class ThreeOutput(val three: ThreePlugin, val meta: Meta = EmptyMeta) : Output<V
val camera = three.buildCamera(meta["camera"].node ?: EmptyMeta)
fun attach(element: HTMLElement, computeWidth: HTMLElement.() -> 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<V
renderer.render(scene, camera)
}
element.appendChild(renderer.domElement)
elementResizeEvent(element) {
renderer.setSize(element.offsetWidth, element.offsetWidth)
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()
}

View File

@ -45,8 +45,9 @@ class ThreeProxyFactory(val three: ThreePlugin) : ThreeFactory<Proxy> {
}
}
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

View File

@ -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<String, VisualObject>()
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<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)
@ -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)
}
}

View File

@ -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
}
}
}
}
}
}
}
}
}
}
}