Finalizing GDML

This commit is contained in:
Alexander Nozik 2019-07-24 20:22:23 +03:00
parent effa19a95e
commit 687393c243
19 changed files with 313 additions and 154 deletions

View File

@ -39,6 +39,9 @@ class DisplayObjectDelegateWrapper<T>(
val write: Config.(name: Name, value: T) -> Unit = { name, value -> set(name, value) },
val read: (MetaItem<*>?) -> T?
) : ReadWriteProperty<VisualObject, T> {
//private var cachedName: Name? = null
override fun getValue(thisRef: VisualObject, property: KProperty<*>): T {
val name = key ?: property.name.toName()
return if (inherited) {

View File

@ -0,0 +1,64 @@
package hep.dataforge.vis.hmr
import kotlin.browser.document
import kotlin.dom.hasClass
external val module: Module
external interface Module {
val hot: Hot?
}
external interface Hot {
val data: dynamic
fun accept()
fun accept(dependency: String, callback: () -> Unit)
fun accept(dependencies: Array<String>, callback: (updated: Array<String>) -> Unit)
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()
}
fun startApplication(builder: () -> ApplicationBase) {
fun start(state: dynamic): ApplicationBase? {
return if (document.body?.hasClass("testApp") == true) {
val application = builder()
@Suppress("UnsafeCastFromDynamic")
application.start(state?.appState ?: emptyMap())
application
} else {
null
}
}
var application: ApplicationBase? = null
val state: dynamic = module.hot?.let { hot ->
hot.accept()
hot.dispose { data ->
data.appState = application?.dispose()
application = null
}
hot.data
}
if (document.body != null) {
application = start(state)
} else {
application = null
document.addEventListener("DOMContentLoaded", { application = start(state) })
}
}

View File

@ -6,7 +6,7 @@
<title>Three js demo for particle physics</title>
<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">
<script type="text/javascript" src="main.bundle.js"></script>
<script type="text/javascript" src="dataforge-vis-spatial-gdml-0.1.0-dev.js"></script>
</head>
<body class="testApp">
<div class="container" id="drop_zone" data-toggle="tooltip" data-placement="right"

View File

@ -7,7 +7,12 @@ kotlin {
val commonMain by getting {
dependencies {
api(project(":dataforge-vis-spatial"))
api("scientifik:gdml:0.1.2")
api("scientifik:gdml:0.1.3")
}
}
val jsMain by getting{
dependencies{
api(project(":dataforge-vis-spatial-js"))
}
}
}

View File

@ -86,7 +86,7 @@ private fun VisualGroup.addSolid(root: GDML, solid: GDMLSolid, block: VisualObje
solid.resolveFirstPosition(root)?.let { applyPosition(it) }
solid.resolveFirstRotation(root)?.let { applyRotation(it) }
}
addSolid(root, second) {}
addSolid(root, second)
solid.resolvePosition(root)?.let { applyPosition(it) }
solid.resolveRotation(root)?.let { applyRotation(it) }
}
@ -96,22 +96,24 @@ private fun VisualGroup.addSolid(root: GDML, solid: GDMLSolid, block: VisualObje
private fun VisualGroup.addVolume(
root: GDML,
gdmlVolume: GDMLVolume,
group: GDMLGroup,
resolveColor: GDMLMaterial.() -> Meta
): VisualGroup {
val solid =
gdmlVolume.solidref.resolve(root)
?: error("Solid with tag ${gdmlVolume.solidref.ref} for volume ${gdmlVolume.name} not defined")
val material =
gdmlVolume.materialref.resolve(root)
?: error("Material with tag ${gdmlVolume.materialref.ref} for volume ${gdmlVolume.name} not defined")
if (group is GDMLVolume) {
val solid = group.solidref.resolve(root)
?: error("Solid with tag ${group.solidref.ref} for volume ${group.name} not defined")
val material = group.materialref.resolve(root)
?: error("Material with tag ${group.materialref.ref} for volume ${group.name} not defined")
addSolid(root, solid) {
color(material.resolveColor())
addSolid(root, solid) {
color(material.resolveColor())
}
//TODO render placements
}
gdmlVolume.physVolumes.forEach {
val volume = it.volumeref.resolve(root) ?: error("Volume with ref ${it.volumeref.ref} could not be resolved")
group.physVolumes.forEach {
val volume: GDMLGroup =
it.volumeref.resolve(root) ?: error("Volume with ref ${it.volumeref.ref} could not be resolved")
addVolume(root, volume, resolveColor).apply {
it.resolvePosition(root)?.let { pos -> applyPosition(pos) }
it.resolveRotation(root)?.let { rot -> applyRotation(rot) }

View File

@ -0,0 +1,82 @@
package hep.dataforge.vis.spatial.gdml.demo
import hep.dataforge.context.Global
import hep.dataforge.vis.hmr.ApplicationBase
import hep.dataforge.vis.hmr.startApplication
import hep.dataforge.vis.spatial.gdml.toVisual
import hep.dataforge.vis.spatial.three.ThreePlugin
import hep.dataforge.vis.spatial.three.output
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import org.w3c.dom.HTMLDivElement
import org.w3c.dom.events.Event
import org.w3c.files.FileList
import org.w3c.files.FileReader
import org.w3c.files.get
import scientifik.gdml.GDML
import kotlin.browser.document
import kotlin.dom.clear
class GDMLDemoApp : ApplicationBase() {
/**
* Handle mouse drag according to https://www.html5rocks.com/en/tutorials/file/dndfiles/
*/
private fun handleDragOver(event: Event) {
event.stopPropagation()
event.preventDefault()
event.asDynamic().dataTransfer.dropEffect = "copy"
}
/**
* Load data from text file
*/
private fun loadData(event: Event, block: suspend (String) -> Unit) {
event.stopPropagation()
event.preventDefault()
val file = (event.asDynamic().dataTransfer.files as FileList)[0]
?: throw RuntimeException("Failed to load file")
FileReader().apply {
onload = {
val string = result as String
GlobalScope.launch {
block(string)
}
}
readAsText(file)
}
}
override fun start(state: Map<String, Any>) {
val context = Global.context("demo") {}
val three = context.plugins.load(ThreePlugin)
val canvas = document.getElementById("canvas") ?: error("Element with id canvas not found on page")
canvas.clear()
val output = three.output(canvas)
//val url = URL("https://drive.google.com/open?id=1w5e7fILMN83JGgB8WANJUYm8OW2s0WVO")
val action: suspend (String) -> Unit = {
val gdml = GDML.format.parse(GDML.serializer(), it)
val visual = gdml.toVisual()
output.render(visual)
}
(document.getElementById("drop_zone") as? HTMLDivElement)?.apply {
addEventListener("dragover", { handleDragOver(it) }, false)
addEventListener("drop", { loadData(it, action) }, false)
}
}
}
fun main() {
startApplication(::GDMLDemoApp)
}

View File

@ -0,0 +1,33 @@
<!DOCTYPE html>
<html>
<head>
<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>
<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">
<script type="text/javascript" src="main.bundle.js"></script>
</head>
<body class="testApp">
<div class="container" id="drop_zone" data-toggle="tooltip" data-placement="right"
title="Для загрузки данных в текстовом формате, надо перетащить файл сюда">
Загрузить данные
<br/>
(перетащить файл сюда)
</div>
<div class="container">
<h1>GDML demo</h1>
</div>
<div class="container" id="canvas"></div>
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
crossorigin="anonymous"></script>
<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>

View File

@ -0,0 +1,28 @@
package hep.dataforge.vis.spatial.gdml
import nl.adaptivity.xmlutil.StAXReader
import org.junit.Test
import scientifik.gdml.GDML
import java.io.File
import java.net.URL
class BMNTest {
@Test
fun testRead() {
val url = URL("https://drive.google.com/open?id=1w5e7fILMN83JGgB8WANJUYm8OW2s0WVO")
val file = File("D:\\Work\\Projects\\gdml.kt\\src\\commonTest\\resources\\gdml\\geofile_full.xml")
val stream = if(file.exists()){
file.inputStream()
} else {
url.openStream()
}
val xmlReader = StAXReader(stream, "UTF-8")
val xml = GDML.format.parse(GDML.serializer(), xmlReader)
repeat(5) {
xml.toVisual()
}
}
}

View File

@ -1,69 +1,21 @@
plugins {
id("scientifik.js")
id("kotlin-dce-js")
//id("kotlin-dce-js")
}
//val kotlinVersion: String by rootProject.extra
dependencies {
api(project(":dataforge-vis-spatial"))
api(project(":dataforge-vis-spatial-gdml"))
api("info.laht.threekt:threejs-wrapper:0.106-npm-2")
api("info.laht.threekt:threejs-wrapper:0.106-npm-3")
testCompile(kotlin("test-js"))
}
kotlin{
sourceSets["main"].dependencies{
api(npm("three","0.106.2"))
implementation(npm("three","0.106.2"))
implementation(npm("@hi-level/three-csg"))
implementation(npm("style-loader"))
implementation(npm("element-resize-event"))
}
}
//
//configure<KotlinFrontendExtension> {
// downloadNodeJsVersion = "latest"
//
// configure<NpmExtension> {
// dependency("three","0.106.2")
// dependency("@hi-level/three-csg")
// dependency("style-loader")
// dependency("element-resize-event")
// devDependency("karma")
// }
//
// sourceMaps = true
//
// bundle<WebPackExtension>("webpack") {
// this as WebPackExtension
// bundleName = "main"
// contentPath = file("src/main/web")
// sourceMapEnabled = true
// //mode = "production"
// mode = "development"
// }
//}
//
//tasks {
// "compileKotlin2Js"(Kotlin2JsCompile::class) {
// kotlinOptions {
// metaInfo = true
// outputFile = "${project.buildDir.path}/js/${project.name}.js"
// sourceMap = true
// moduleKind = "commonjs"
// main = "call"
// kotlinOptions.sourceMapEmbedSources = "always"
// }
// }
//
// "compileTestKotlin2Js"(Kotlin2JsCompile::class) {
// kotlinOptions {
// metaInfo = true
// outputFile = "${project.buildDir.path}/js/${project.name}-test.js"
// sourceMap = true
// moduleKind = "commonjs"
// kotlinOptions.sourceMapEmbedSources = "always"
// }
// }
//}

View File

@ -1,19 +0,0 @@
package hep.dataforge.vis.spatial.demo
external val module: Module
external interface Module {
val hot: Hot?
}
external interface Hot {
val data: dynamic
fun accept()
fun accept(dependency: String, callback: () -> Unit)
fun accept(dependencies: Array<String>, callback: (updated: Array<String>) -> Unit)
fun dispose(callback: (data: dynamic) -> Unit)
}
external fun require(name: String): dynamic

View File

@ -4,6 +4,8 @@ import hep.dataforge.context.ContextBuilder
import hep.dataforge.meta.number
import hep.dataforge.vis.common.Colors
import hep.dataforge.vis.common.color
import hep.dataforge.vis.hmr.ApplicationBase
import hep.dataforge.vis.hmr.startApplication
import hep.dataforge.vis.spatial.*
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
@ -136,3 +138,7 @@ class ThreeDemoApp : ApplicationBase() {
override fun dispose() = emptyMap<String, Any>()//mapOf("lines" to presenter.dispose())
}
fun main() {
startApplication(::ThreeDemoApp)
}

View File

@ -50,7 +50,7 @@ class ThreeDemoGrid(meta: Meta) : AbstractPlugin(meta), OutputManager {
return outputs.getOrPut(name) {
if (type != VisualObject::class) error("Supports only DisplayObject")
val output = three.output(meta) {
val output = three.output(meta = meta) {
"axis" to {
"size" to 500
}

View File

@ -1,48 +0,0 @@
package hep.dataforge.vis.spatial.demo
import kotlin.browser.document
import kotlin.dom.hasClass
abstract class ApplicationBase {
abstract val stateKeys: List<String>
abstract fun start(state: Map<String, Any>)
abstract fun dispose(): Map<String, Any>
}
fun main() {
var application: ApplicationBase? = null
val state: dynamic = module.hot?.let { hot ->
hot.accept()
hot.dispose { data ->
data.appState = application?.dispose()
application = null
}
hot.data
}
if (document.body != null) {
application = start(state)
} else {
application = null
document.addEventListener("DOMContentLoaded", { application = start(state) })
}
}
fun start(state: dynamic): ApplicationBase? {
return if (document.body?.hasClass("testApp") == true) {
val application = ThreeDemoApp()
@Suppress("UnsafeCastFromDynamic")
application.start(state?.appState ?: emptyMap<String, Any>())
application
} else {
null
}
}

View File

@ -0,0 +1,32 @@
package hep.dataforge.vis.spatial.three
import hep.dataforge.vis.spatial.Cylinder
import hep.dataforge.vis.spatial.detail
import info.laht.threekt.core.BufferGeometry
import info.laht.threekt.geometries.CylinderBufferGeometry
import kotlin.math.pow
object ThreeCylinderFactory : MeshThreeFactory<Cylinder>(Cylinder::class) {
override fun buildGeometry(obj: Cylinder): BufferGeometry {
return obj.detail?.let {
val segments = it.toDouble().pow(0.5).toInt()
CylinderBufferGeometry(
radiusTop = obj.upperRadius!!,
radiusBottom = obj.radius!!,
height = obj.height!!,
radialSegments = segments,
heightSegments = segments,
openEnded = false,
thetaStart = obj.startAngle,
thetaLength = obj.angle
)
} ?: CylinderBufferGeometry(
radiusTop = obj.upperRadius!!,
radiusBottom = obj.radius!!,
height = obj.height!!,
openEnded = false,
thetaStart = obj.startAngle,
thetaLength = obj.angle
)
}
}

View File

@ -5,7 +5,7 @@ import hep.dataforge.meta.*
import hep.dataforge.output.Output
import hep.dataforge.vis.common.Colors
import hep.dataforge.vis.common.VisualObject
import hep.dataforge.vis.spatial.demo.require
import hep.dataforge.vis.hmr.require
import info.laht.threekt.WebGLRenderer
import info.laht.threekt.helpers.AxesHelper
import info.laht.threekt.lights.AmbientLight
@ -39,7 +39,7 @@ class ThreeOutput(val three: ThreePlugin, val meta: Meta = EmptyMeta) : Output<V
setSize(width, height)
}
three.addControls(camera,renderer.domElement, meta["controls"].node?:EmptyMeta)
three.addControls(camera, renderer.domElement, meta["controls"].node ?: EmptyMeta)
fun animate() {
window.requestAnimationFrame {
@ -63,5 +63,9 @@ class ThreeOutput(val three: ThreePlugin, val meta: Meta = EmptyMeta) : Output<V
}
}
fun ThreePlugin.output(meta: Meta = EmptyMeta, override: MetaBuilder.() -> Unit = {}) =
ThreeOutput(this, buildMeta(meta, override))
fun ThreePlugin.output(element: Element? = null, meta: Meta = EmptyMeta, override: MetaBuilder.() -> Unit = {}) =
ThreeOutput(this, buildMeta(meta, override)).apply {
if(element!=null){
attach(element)
}
}

View File

@ -28,6 +28,7 @@ class ThreePlugin : AbstractPlugin() {
objectFactories[Box::class] = ThreeBoxFactory
objectFactories[Convex::class] = ThreeConvexFactory
objectFactories[Sphere::class] = ThreeSphereFactory
objectFactories[Cylinder::class] = ThreeCylinderFactory
}
private fun findObjectFactory(type: KClass<out VisualObject>): ThreeFactory<*>? {

View File

@ -25,7 +25,9 @@ fun VisualGroup.composite(type: CompositeType, builder: VisualGroup.() -> Unit):
val group = VisualGroup().apply(builder)
val children = group.toList()
if (children.size != 2) error("Composite requires exactly two children")
return Composite(this, children[0], children[1], type, group.properties.seal()).also { add(it) }
return Composite(this, children[0], children[1], type, group.properties.seal()).also {
this.add(it)
}
}
fun VisualGroup.union(builder: VisualGroup.() -> Unit) =

View File

@ -16,13 +16,13 @@ class Cylinder(parent: VisualObject?, meta: Meta) : DisplayLeaf(parent, meta) {
var upperRadius by number(default = radius)
var height by number()
var startAngle by number(0.0)
var angle by number(2* PI)
var angle by number(2 * PI)
}
fun VisualGroup.cylinder(r: Number, height: Number, meta: Meta = EmptyMeta, block: Cylinder.()->Unit = {}):Cylinder{
val cylinder = Cylinder(this,meta)
fun VisualGroup.cylinder(r: Number, height: Number, meta: Meta = EmptyMeta, block: Cylinder.() -> Unit = {}): Cylinder {
val cylinder = Cylinder(this, meta)
cylinder.radius = r
cylinder.height = height
cylinder.apply(block)
return cylinder
return cylinder.also { add(it) }
}

View File

@ -1,6 +1,7 @@
package hep.dataforge.vis.spatial
import hep.dataforge.meta.*
import hep.dataforge.names.toName
import hep.dataforge.output.Output
import hep.dataforge.vis.common.VisualGroup
import hep.dataforge.vis.common.VisualObject
@ -28,60 +29,71 @@ var VisualObject.visible
// 3D Object position
private val xPos = "pos.x".toName()
/**
* x position property relative to parent. Not inherited
*/
var VisualObject.x
get() = properties["pos.x"].number ?: 0.0
get() = properties[xPos].number ?: 0.0
set(value) {
properties["pos.x"] = value
properties[xPos] = value
}
private val yPos = "pos.y".toName()
/**
* y position property. Not inherited
*/
var VisualObject.y
get() = properties["pos.y"].number ?: 0.0
get() = properties[yPos].number ?: 0.0
set(value) {
properties["pos.y"] = value
properties[yPos] = value
}
private val zPos = "pos.z".toName()
/**
* z position property. Not inherited
*/
var VisualObject.z
get() = properties["pos.z"].number ?: 0.0
get() = properties[zPos].number ?: 0.0
set(value) {
properties["pos.z"] = value
properties[zPos] = value
}
// 3D Object rotation
private val xRotation = "rotation.x".toName()
/**
* x rotation relative to parent. Not inherited
*/
var VisualObject.rotationX
get() = properties["rotation.x"].number ?: 0.0
get() = properties[xRotation].number ?: 0.0
set(value) {
properties["rotation.x"] = value
properties[xRotation] = value
}
private val yRotation = "rotation.y".toName()
/**
* y rotation relative to parent. Not inherited
*/
var VisualObject.rotationY
get() = properties["rotation.y"].number ?: 0.0
get() = properties[yRotation].number ?: 0.0
set(value) {
properties["rotation.y"] = value
properties[yRotation] = value
}
private val zRotation = "rotation.z".toName()
/**
* z rotation relative to parent. Not inherited
*/
var VisualObject.rotationZ
get() = properties["rotation.z"].number ?: 0.0
get() = properties[zRotation].number ?: 0.0
set(value) {
properties["rotation.z"] = value
properties[zRotation] = value
}
enum class RotationOrder {