Tree viewer replacement

This commit is contained in:
Alexander Nozik 2019-10-13 12:38:24 +03:00
parent 017995e045
commit 1d5debb8a6
15 changed files with 183 additions and 185 deletions

View File

@ -17,6 +17,9 @@ allprojects {
repositories { repositories {
maven("https://dl.bintray.com/pdvrieze/maven") maven("https://dl.bintray.com/pdvrieze/maven")
maven("http://maven.jzy3d.org/releases") maven("http://maven.jzy3d.org/releases")
maven("https://kotlin.bintray.com/js-externals")
// maven("https://dl.bintray.com/gbaldeck/kotlin")
// maven("https://dl.bintray.com/rjaros/kotlin")
} }
group = "hep.dataforge" group = "hep.dataforge"

View File

@ -10,6 +10,7 @@ scientifik{
} }
val dataforgeVersion: String by rootProject.extra val dataforgeVersion: String by rootProject.extra
//val kvisionVersion: String by rootProject.extra("2.0.0-M1")
kotlin { kotlin {
jvm{ jvm{
@ -31,9 +32,9 @@ kotlin {
jsMain{ jsMain{
dependencies { dependencies {
api("hep.dataforge:dataforge-output-html:$dataforgeVersion") api("hep.dataforge:dataforge-output-html:$dataforgeVersion")
api("kotlin.js.externals:kotlin-js-jquery:3.2.0-0")
api(npm("text-encoding")) api(npm("text-encoding"))
api("org.jetbrains:kotlin-extensions:1.0.1-pre.83-kotlin-1.3.50") api(npm("bootstrap","4.3.1"))
api(npm("core-js"))
} }
} }
} }

View File

@ -52,6 +52,8 @@ interface VisualGroup : Provider, Iterable<VisualObject>, VisualObject {
} }
} }
val VisualGroup.isEmpty: Boolean get() = this.children.isEmpty()
interface MutableVisualGroup : VisualGroup { interface MutableVisualGroup : VisualGroup {
/** /**

View File

@ -1,14 +1,10 @@
package hep.dataforge.vis package hep.dataforge.js
import kotlin.browser.document import kotlin.browser.document
import kotlin.dom.hasClass import kotlin.dom.hasClass
external val module: Module external val module: Module
external interface Module {
val hot: Hot?
}
external interface Hot { external interface Hot {
val data: dynamic val data: dynamic
@ -19,17 +15,31 @@ external interface Hot {
fun dispose(callback: (data: dynamic) -> Unit) fun dispose(callback: (data: dynamic) -> Unit)
} }
external fun require(name: String): dynamic external interface Module {
val hot: Hot?
abstract class ApplicationBase {
open val stateKeys: List<String> get() = emptyList()
abstract fun start(state: Map<String, Any>)
open fun dispose(): Map<String, Any> = emptyMap()
} }
fun startApplication(builder: () -> ApplicationBase) { /**
fun start(state: dynamic): ApplicationBase? { * Base interface for applications.
*
* Base interface for applications supporting Hot Module Replacement (HMR).
*/
interface Application {
/**
* Starting point for an application.
* @param state Initial state between Hot Module Replacement (HMR).
*/
fun start(state: Map<String, Any>)
/**
* Ending point for an application.
* @return final state for Hot Module Replacement (HMR).
*/
fun dispose(): Map<String, Any> = emptyMap()
}
fun startApplication(builder: () -> Application) {
fun start(state: dynamic): Application? {
return if (document.body?.hasClass("testApp") == true) { return if (document.body?.hasClass("testApp") == true) {
val application = builder() val application = builder()
@ -42,7 +52,7 @@ fun startApplication(builder: () -> ApplicationBase) {
} }
} }
var application: ApplicationBase? = null var application: Application? = null
val state: dynamic = module.hot?.let { hot -> val state: dynamic = module.hot?.let { hot ->
hot.accept() hot.accept()

View File

@ -1,4 +1,6 @@
package hep.dataforge.vis package hep.dataforge.js
external fun require(name: String): dynamic
inline fun <T : Any> jsObject(builder: T.() -> Unit): T { inline fun <T : Any> jsObject(builder: T.() -> Unit): T {
val obj: T = js("({})") as T val obj: T = js("({})") as T

View File

@ -0,0 +1,23 @@
/* Remove default bullets */
ul, .objTree-subtree {
list-style-type: none;
}
/* Style the caret/arrow */
.objTree-caret {
cursor: pointer;
user-select: none; /* Prevent text selection */
}
/* Create the caret/arrow with a unicode, and style it */
.objTree-caret::before {
content: "\25B6";
color: black;
display: inline-block;
margin-right: 6px;
}
/* Rotate the caret/arrow icon when clicked on (using JavaScript) */
.objTree-caret-down::before {
transform: rotate(90deg);
}

View File

@ -1,7 +1,10 @@
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.vis.ApplicationBase import hep.dataforge.js.Application
import hep.dataforge.js.objectTree
import hep.dataforge.js.startApplication
import hep.dataforge.names.NameToken
import hep.dataforge.vis.spatial.Material3D.Companion.OPACITY_KEY import hep.dataforge.vis.spatial.Material3D.Companion.OPACITY_KEY
import hep.dataforge.vis.spatial.Visual3DPlugin import hep.dataforge.vis.spatial.Visual3DPlugin
import hep.dataforge.vis.spatial.VisualGroup3D import hep.dataforge.vis.spatial.VisualGroup3D
@ -9,13 +12,11 @@ import hep.dataforge.vis.spatial.VisualObject3D
import hep.dataforge.vis.spatial.attachChildren import hep.dataforge.vis.spatial.attachChildren
import hep.dataforge.vis.spatial.editor.propertyEditor import hep.dataforge.vis.spatial.editor.propertyEditor
import hep.dataforge.vis.spatial.editor.threeOutputConfig import hep.dataforge.vis.spatial.editor.threeOutputConfig
import hep.dataforge.vis.spatial.editor.visualObjectTree
import hep.dataforge.vis.spatial.gdml.GDMLTransformer import hep.dataforge.vis.spatial.gdml.GDMLTransformer
import hep.dataforge.vis.spatial.gdml.LUnit import hep.dataforge.vis.spatial.gdml.LUnit
import hep.dataforge.vis.spatial.gdml.toVisual import hep.dataforge.vis.spatial.gdml.toVisual
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.startApplication
import kotlinx.html.dom.append import kotlinx.html.dom.append
import kotlinx.html.js.p import kotlinx.html.js.p
import org.w3c.dom.DragEvent import org.w3c.dom.DragEvent
@ -29,7 +30,7 @@ import kotlin.browser.document
import kotlin.browser.window import kotlin.browser.window
import kotlin.dom.clear import kotlin.dom.clear
private class GDMLDemoApp : ApplicationBase() { private class GDMLDemoApp : Application {
/** /**
* Handle mouse drag according to https://www.html5rocks.com/en/tutorials/file/dndfiles/ * Handle mouse drag according to https://www.html5rocks.com/en/tutorials/file/dndfiles/
*/ */
@ -46,7 +47,6 @@ private class GDMLDemoApp : ApplicationBase() {
event.stopPropagation() event.stopPropagation()
event.preventDefault() event.preventDefault()
val file = (event.dataTransfer?.files as FileList)[0] val file = (event.dataTransfer?.files as FileList)[0]
?: throw RuntimeException("Failed to load file") ?: throw RuntimeException("Failed to load file")
@ -153,7 +153,9 @@ private class GDMLDemoApp : ApplicationBase() {
output.camera.layers.set(0) output.camera.layers.set(0)
layers.threeOutputConfig(output) layers.threeOutputConfig(output)
tree.visualObjectTree(visual, editor::propertyEditor) //tree.visualObjectTree(visual, editor::propertyEditor)
tree.objectTree(NameToken("World"),visual, editor::propertyEditor)
output.render(visual) output.render(visual)
message(null) message(null)
@ -166,10 +168,6 @@ private class GDMLDemoApp : ApplicationBase() {
} }
} }
override fun dispose(): Map<String, Any> {
return super.dispose()
}
} }
fun main() { fun main() {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -11,3 +11,38 @@
0% { transform: rotate(0deg); } 0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); } 100% { transform: rotate(360deg); }
} }
/* Remove default bullets */
ul, .objTree-subtree {
list-style-type: none;
}
/* Style the caret/arrow */
.objTree-caret {
cursor: pointer;
user-select: none; /* Prevent text selection */
}
.objTree-label {
cursor: pointer;
}
/* Create the caret/arrow with a unicode, and style it */
.objTree-caret::before {
content: "\25B6";
color: black;
display: inline-block;
margin-right: 6px;
}
.objTree-leaf::before {
content: "\25C6";
color: black;
display: inline-block;
margin-right: 6px;
}
/* Rotate the caret/arrow icon when clicked on (using JavaScript) */
.objTree-caret-down::before {
transform: rotate(90deg);
}

View File

@ -4,13 +4,9 @@
<meta charset="utf-8"> <meta charset="utf-8">
<!-- <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">--> <!-- <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">-->
<title>Three js demo for particle physics</title> <title>Three js demo for particle physics</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"> integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<link rel="stylesheet" href="css/main.css"> <link rel="stylesheet" href="css/main.css">
<link rel="stylesheet" href="css/inspire-tree-light.min.css">
<link rel="stylesheet" href="css/jsoneditor.min.css"> <link rel="stylesheet" href="css/jsoneditor.min.css">
<script type="text/javascript" src="main.bundle.js"></script> <script type="text/javascript" src="main.bundle.js"></script>
</head> </head>
@ -37,12 +33,5 @@
<div class="col-lg-3" id="editor"></div> <div class="col-lg-3" id="editor"></div>
</div> </div>
</div> </div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"
integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1"
crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"
integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM"
crossorigin="anonymous"></script>
</body> </body>
</html> </html>

View File

@ -0,0 +1,75 @@
package hep.dataforge.js
import hep.dataforge.names.NameToken
import hep.dataforge.vis.common.VisualGroup
import hep.dataforge.vis.common.VisualObject
import hep.dataforge.vis.common.isEmpty
import hep.dataforge.vis.spatial.editor.card
import kotlinx.html.TagConsumer
import kotlinx.html.dom.append
import kotlinx.html.js.*
import org.w3c.dom.Element
import org.w3c.dom.HTMLElement
import org.w3c.dom.HTMLSpanElement
import kotlin.dom.clear
fun Element.objectTree(
token: NameToken,
obj: VisualObject,
clickCallback: (VisualObject) -> Unit = {}
) {
clear()
append {
card("Object tree") {
subTree(token, obj, clickCallback)
}
}
}
private fun TagConsumer<HTMLElement>.subTree(
token: NameToken,
obj: VisualObject,
clickCallback: (VisualObject) -> Unit
) {
if (obj is VisualGroup && !obj.isEmpty) {
lateinit var toggle: HTMLSpanElement
div("d-inline-block text-truncate") {
toggle = span("objTree-caret")
label("objTree-label") {
+token.toString()
onClickFunction = { clickCallback(obj) }
}
}
val subtree = ul("objTree-subtree")
toggle.onclick = {
toggle.classList.toggle("objTree-caret-down")
subtree.apply {
if (toggle.classList.contains("objTree-caret-down")) {
obj.children.entries
.filter { !it.key.toString().startsWith("@") }
.sortedBy { (it.value as? VisualGroup)?.isEmpty ?: true }
.forEach { (token, child) ->
append {
li().apply {
subTree(token, child, clickCallback)
}
}
}
} else {
this.clear()
}
}
//jQuery(subtree).asDynamic().collapse("toggle")
}
} else {
div("d-inline-block text-truncate") {
span("objTree-leaf")
label("objTree-label") {
+token.toString()
onClickFunction = { clickCallback(obj) }
}
}
}
}

View File

@ -1,136 +0,0 @@
@file:Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE")
package hep.dataforge.vis.spatial.editor
import hep.dataforge.meta.string
import hep.dataforge.names.EmptyName
import hep.dataforge.names.Name
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.jsObject
import hep.dataforge.vis.spatial.Proxy
import hep.dataforge.vis.spatial.visible
import info.laht.threekt.loaders.Cache.clear
import kotlinx.html.div
import kotlinx.html.dom.append
import org.w3c.dom.Element
import kotlin.js.json
operator fun Name.plus(other: NameToken): Name = Name(tokens + other)
private fun createInspireTree(block: Config.() -> Unit = {}): InspireTree {
val config = (json(
"checkbox" to json(
"autoCheckChildren" to false
)
) as Config).apply(block)
return InspireTree(config)
}
private fun VisualObject.toTree(onFocus: (VisualObject?, String?) -> Unit = { _, _ -> }): InspireTree {
val map = HashMap<String, VisualObject>()
fun generateNodeConfig(item: VisualObject, fullName: Name): NodeConfig {
val title = item.getProperty("title").string ?: fullName.last()?.toString() ?: "root"
val className = if (item is Proxy) {
item.prototype::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>()
} else {
true
},
"text" to text,
"id" to fullName.toString(),
"itree" to json(
"state" to json(
"checked" to (item.visible ?: true)
)
)
) as NodeConfig
}
fun TreeNode.fillChildren(group: VisualObject, groupName: Name) {
if(group is VisualGroup) {
group.children.forEach { (token, obj) ->
if (!token.body.startsWith("@")) {
val name = groupName + token
val nodeConfig = generateNodeConfig(obj, name)
val childNode = addChild(nodeConfig)
map[childNode.id] = obj
if (obj is VisualGroup) {
childNode.fillChildren(obj, name)
}
}
}
}
}
val inspireTree = createInspireTree {
}
val nodeConfig = generateNodeConfig(this, EmptyName)
val rootNode = inspireTree.addNode(nodeConfig)
map[rootNode.id] = this
rootNode.fillChildren(this, EmptyName)
// inspireTree.on("node.selected") { node: TreeNode, isLoadEvent: Boolean ->
// if (!isLoadEvent) {
// map[node.id]?.selected = node.selected()
// }
// }
//
// inspireTree.on("node.deselect") { node: TreeNode ->
// map[node.id]?.selected = node.selected()
// }
inspireTree.on("node.checked") { node: TreeNode, isLoadEvent: Boolean ->
if (!isLoadEvent) {
map[node.id]?.visible = node.checked()
}
}
inspireTree.on("node.unchecked") { node: TreeNode ->
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)
}
}
inspireTree.collapseDeep()
return inspireTree
}
fun Element.visualObjectTree(group: VisualObject, onFocus: (VisualObject?, String?) -> Unit) {
clear()
append {
card("Visual object tree") {
val domConfig = jsObject<DomConfig> {
target = div()
showCheckboxes = false
}
InspireTreeDOM(group.toTree(onFocus), domConfig)
}
}
}

View File

@ -1,10 +1,10 @@
package hep.dataforge.vis.spatial.editor package hep.dataforge.vis.spatial.editor
import hep.dataforge.io.toJson import hep.dataforge.io.toJson
import hep.dataforge.js.jsObject
import hep.dataforge.meta.* import hep.dataforge.meta.*
import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.common.VisualObject
import hep.dataforge.vis.common.findStyle import hep.dataforge.vis.common.findStyle
import hep.dataforge.vis.jsObject
import hep.dataforge.vis.spatial.Material3D.Companion.COLOR_KEY import hep.dataforge.vis.spatial.Material3D.Companion.COLOR_KEY
import hep.dataforge.vis.spatial.Material3D.Companion.OPACITY_KEY import hep.dataforge.vis.spatial.Material3D.Companion.OPACITY_KEY
import hep.dataforge.vis.spatial.VisualObject3D.Companion.VISIBLE_KEY import hep.dataforge.vis.spatial.VisualObject3D.Companion.VISIBLE_KEY
@ -22,7 +22,7 @@ import kotlin.dom.clear
fun Meta.toDynamic() = JSON.parse<dynamic>(toJson().toString()) fun Meta.toDynamic() = JSON.parse<dynamic>(toJson().toString())
fun Element.propertyEditor(item: VisualObject?, name: String?) { fun Element.propertyEditor(item: VisualObject?) {
clear() clear()
if (item != null) { if (item != null) {
append { append {

View File

@ -1,10 +1,10 @@
package hep.dataforge.vis.spatial.demo package hep.dataforge.vis.spatial.demo
import hep.dataforge.context.ContextBuilder import hep.dataforge.context.ContextBuilder
import hep.dataforge.vis.ApplicationBase import hep.dataforge.js.Application
import hep.dataforge.js.startApplication
import hep.dataforge.vis.common.Colors import hep.dataforge.vis.common.Colors
import hep.dataforge.vis.spatial.* import hep.dataforge.vis.spatial.*
import hep.dataforge.vis.startApplication
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive import kotlinx.coroutines.isActive
@ -15,9 +15,7 @@ import kotlin.math.sin
import kotlin.random.Random import kotlin.random.Random
private class ThreeDemoApp : ApplicationBase() { private class ThreeDemoApp : Application {
override val stateKeys: List<String> = emptyList()
override fun start(state: Map<String, Any>) { override fun start(state: Map<String, Any>) {
@ -141,7 +139,7 @@ private class ThreeDemoApp : ApplicationBase() {
thickness = 208.0 thickness = 208.0
rotationX = it * PI2 / 20 rotationX = it * PI2 / 20
color(Colors.green) color(Colors.green)
//rotationY = it * PI2 / 20 //rotationY = it * PI2 / 20
} }
} }
} }