forked from kscience/visionforge
Tree viewer replacement
This commit is contained in:
parent
017995e045
commit
1d5debb8a6
@ -17,6 +17,9 @@ allprojects {
|
||||
repositories {
|
||||
maven("https://dl.bintray.com/pdvrieze/maven")
|
||||
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"
|
||||
|
@ -10,6 +10,7 @@ scientifik{
|
||||
}
|
||||
|
||||
val dataforgeVersion: String by rootProject.extra
|
||||
//val kvisionVersion: String by rootProject.extra("2.0.0-M1")
|
||||
|
||||
kotlin {
|
||||
jvm{
|
||||
@ -31,9 +32,9 @@ kotlin {
|
||||
jsMain{
|
||||
dependencies {
|
||||
api("hep.dataforge:dataforge-output-html:$dataforgeVersion")
|
||||
api("kotlin.js.externals:kotlin-js-jquery:3.2.0-0")
|
||||
api(npm("text-encoding"))
|
||||
api("org.jetbrains:kotlin-extensions:1.0.1-pre.83-kotlin-1.3.50")
|
||||
api(npm("core-js"))
|
||||
api(npm("bootstrap","4.3.1"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -52,6 +52,8 @@ interface VisualGroup : Provider, Iterable<VisualObject>, VisualObject {
|
||||
}
|
||||
}
|
||||
|
||||
val VisualGroup.isEmpty: Boolean get() = this.children.isEmpty()
|
||||
|
||||
interface MutableVisualGroup : VisualGroup {
|
||||
|
||||
/**
|
||||
|
@ -1,14 +1,10 @@
|
||||
package hep.dataforge.vis
|
||||
package hep.dataforge.js
|
||||
|
||||
import kotlin.browser.document
|
||||
import kotlin.dom.hasClass
|
||||
|
||||
external val module: Module
|
||||
|
||||
external interface Module {
|
||||
val hot: Hot?
|
||||
}
|
||||
|
||||
external interface Hot {
|
||||
val data: dynamic
|
||||
|
||||
@ -19,17 +15,31 @@ external interface Hot {
|
||||
fun dispose(callback: (data: dynamic) -> Unit)
|
||||
}
|
||||
|
||||
external fun require(name: String): dynamic
|
||||
|
||||
abstract class ApplicationBase {
|
||||
open val stateKeys: List<String> get() = emptyList()
|
||||
|
||||
abstract fun start(state: Map<String, Any>)
|
||||
open fun dispose(): Map<String, Any> = emptyMap()
|
||||
external interface Module {
|
||||
val hot: Hot?
|
||||
}
|
||||
|
||||
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) {
|
||||
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 ->
|
||||
hot.accept()
|
@ -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 {
|
||||
val obj: T = js("({})") as T
|
23
dataforge-vis-common/src/jsMain/resources/css/common.css
Normal file
23
dataforge-vis-common/src/jsMain/resources/css/common.css
Normal 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);
|
||||
}
|
@ -1,7 +1,10 @@
|
||||
package hep.dataforge.vis.spatial.gdml.demo
|
||||
|
||||
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.Visual3DPlugin
|
||||
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.editor.propertyEditor
|
||||
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.LUnit
|
||||
import hep.dataforge.vis.spatial.gdml.toVisual
|
||||
import hep.dataforge.vis.spatial.three.ThreePlugin
|
||||
import hep.dataforge.vis.spatial.three.output
|
||||
import hep.dataforge.vis.startApplication
|
||||
import kotlinx.html.dom.append
|
||||
import kotlinx.html.js.p
|
||||
import org.w3c.dom.DragEvent
|
||||
@ -29,7 +30,7 @@ import kotlin.browser.document
|
||||
import kotlin.browser.window
|
||||
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/
|
||||
*/
|
||||
@ -46,7 +47,6 @@ private class GDMLDemoApp : ApplicationBase() {
|
||||
event.stopPropagation()
|
||||
event.preventDefault()
|
||||
|
||||
|
||||
val file = (event.dataTransfer?.files as FileList)[0]
|
||||
?: throw RuntimeException("Failed to load file")
|
||||
|
||||
@ -153,7 +153,9 @@ private class GDMLDemoApp : ApplicationBase() {
|
||||
|
||||
output.camera.layers.set(0)
|
||||
layers.threeOutputConfig(output)
|
||||
tree.visualObjectTree(visual, editor::propertyEditor)
|
||||
//tree.visualObjectTree(visual, editor::propertyEditor)
|
||||
tree.objectTree(NameToken("World"),visual, editor::propertyEditor)
|
||||
|
||||
|
||||
output.render(visual)
|
||||
message(null)
|
||||
@ -166,10 +168,6 @@ private class GDMLDemoApp : ApplicationBase() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun dispose(): Map<String, Any> {
|
||||
return super.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -11,3 +11,38 @@
|
||||
0% { transform: rotate(0deg); }
|
||||
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);
|
||||
}
|
||||
|
@ -4,13 +4,9 @@
|
||||
<meta charset="utf-8">
|
||||
<!-- <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">-->
|
||||
<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"
|
||||
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
|
||||
<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">
|
||||
<script type="text/javascript" src="main.bundle.js"></script>
|
||||
</head>
|
||||
@ -37,12 +33,5 @@
|
||||
<div class="col-lg-3" id="editor"></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>
|
||||
</html>
|
@ -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) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
package hep.dataforge.vis.spatial.editor
|
||||
|
||||
import hep.dataforge.io.toJson
|
||||
import hep.dataforge.js.jsObject
|
||||
import hep.dataforge.meta.*
|
||||
import hep.dataforge.vis.common.VisualObject
|
||||
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.OPACITY_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 Element.propertyEditor(item: VisualObject?, name: String?) {
|
||||
fun Element.propertyEditor(item: VisualObject?) {
|
||||
clear()
|
||||
if (item != null) {
|
||||
append {
|
||||
|
@ -1,10 +1,10 @@
|
||||
package hep.dataforge.vis.spatial.demo
|
||||
|
||||
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.spatial.*
|
||||
import hep.dataforge.vis.startApplication
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.isActive
|
||||
@ -15,9 +15,7 @@ import kotlin.math.sin
|
||||
import kotlin.random.Random
|
||||
|
||||
|
||||
private class ThreeDemoApp : ApplicationBase() {
|
||||
|
||||
override val stateKeys: List<String> = emptyList()
|
||||
private class ThreeDemoApp : Application {
|
||||
|
||||
override fun start(state: Map<String, Any>) {
|
||||
|
||||
@ -141,7 +139,7 @@ private class ThreeDemoApp : ApplicationBase() {
|
||||
thickness = 208.0
|
||||
rotationX = it * PI2 / 20
|
||||
color(Colors.green)
|
||||
//rotationY = it * PI2 / 20
|
||||
//rotationY = it * PI2 / 20
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user