Compare commits
161 Commits
v0.1.5-dev
...
v0.2.0-dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f6459ec07f | ||
| e50e266f94 | |||
| 01ea6b5494 | |||
| 87260cea86 | |||
| a87692ae1f | |||
| c1627b4504 | |||
| 493c527743 | |||
| fb358dbc8e | |||
|
|
7242b5b4e0 | ||
|
|
a5466dca0c | ||
|
|
95220422cc | ||
| e1138be861 | |||
| 38c76e824d | |||
| 8f95d6f485 | |||
| f2e7e16d62 | |||
| 0d53b6f77d | |||
| 5af8fe3e99 | |||
| 073d374a9e | |||
| 42e2530f6f | |||
| 7b30b62849 | |||
| 2b6942b827 | |||
| ba637413c7 | |||
| c82c0ecea3 | |||
| 199cad1dc1 | |||
| 18b6ac2c37 | |||
|
|
32dad9d8b5 | ||
|
|
3fdd0d62d4 | ||
| a7841edb3c | |||
| a583030c1c | |||
| 230cd5dc61 | |||
| b0cd80a4e6 | |||
| 7cf819d7ce | |||
| ea5f0abbf6 | |||
| 9cc3f65a18 | |||
| 9b99df88a5 | |||
| 91e7e8573e | |||
| dfc0ffda38 | |||
| bd1f7d75fc | |||
| 288307eaa8 | |||
| 359eb05f83 | |||
| fb3ff4d6fc | |||
| bdbff04d78 | |||
| 8652b03fa5 | |||
| 1f8700efde | |||
| 5afa9117c4 | |||
| b8fadb06b5 | |||
| e615fcc2bc | |||
| 65c0183ba7 | |||
| faf3fa8512 | |||
| 2798439582 | |||
| 03b666017b | |||
| e644b1b20a | |||
| ff80629f24 | |||
| 3a87e43483 | |||
| 8a41f185de | |||
| 0961ae90b7 | |||
| 97b988d419 | |||
| 1a5a207fb7 | |||
| 8060933859 | |||
| 7febb4452a | |||
| 3f6e39b18d | |||
| 545753b14b | |||
| 30e6370204 | |||
| 0e488563a9 | |||
| 78a04728ba | |||
| 720555a942 | |||
| 7c9315b029 | |||
| bc15d9241b | |||
| a5bfa0f147 | |||
| 27fe492ab7 | |||
| 5d0ceb8e50 | |||
| 60906db32e | |||
| 1da4381a4c | |||
| e82ea7bb94 | |||
| 2b7971eeea | |||
| dab93fc136 | |||
| 19513656bd | |||
| 4820082248 | |||
| f7301805fd | |||
| 18828e87f2 | |||
| 5eb9925d35 | |||
| 716487d70a | |||
| bb6c6e2175 | |||
| 0c1d6139ae | |||
| 33146fef1b | |||
| 3f9f253416 | |||
|
|
f0f117d4fc | ||
| 51af6b9378 | |||
| 56481933ed | |||
| 1e183107cf | |||
| 346b61724f | |||
| 2b02f151d2 | |||
| 25a47a9719 | |||
| 3c0df98f50 | |||
| 216be4a6a1 | |||
| 66ea23ad60 | |||
| eefc036dcb | |||
| 0259d4eb15 | |||
| 4c235b0f53 | |||
| ccb916cff7 | |||
| 6939fba292 | |||
| be1d79c229 | |||
| 97cdfd3719 | |||
| 6a6d9659ca | |||
| 929832f3a5 | |||
| b2ba92e745 | |||
| 85bb690699 | |||
| c83954dca9 | |||
| 1ccd45d6c5 | |||
| 382686b9aa | |||
| f4970955cb | |||
| 651a875eee | |||
| 70ac2c99dd | |||
| 734d1e1168 | |||
| a38d70bade | |||
| 6a742658af | |||
| faddb8a393 | |||
| a7136d3eff | |||
| 8a4779e9c4 | |||
| face7bfd0c | |||
| a85cd828e6 | |||
| 9b42d4f186 | |||
| ebb7bf72d1 | |||
| 2be4576495 | |||
| 5e340aa179 | |||
| a44e8091c8 | |||
| e44096e844 | |||
| 698f74adaa | |||
| d1607459a1 | |||
| dc805228b4 | |||
| d2553d5b40 | |||
| e01a688664 | |||
| cae3ab00d9 | |||
| 6a48948c15 | |||
| 613624ff17 | |||
| 62a76f2c86 | |||
| 7e037b57fc | |||
| dfac01d352 | |||
| fdc221dfa1 | |||
| 5b8d298ac6 | |||
| 6b4bc6912f | |||
| 8f6c3822ff | |||
| 7571b58e99 | |||
| aaad836567 | |||
| 3edb00b6bf | |||
| 22c3521a9e | |||
| 072b036fa2 | |||
| f72d626b0b | |||
| c3001cd858 | |||
| 6116523091 | |||
| 9361145197 | |||
| c83a25b0a1 | |||
| 68cf4748d8 | |||
| ea82082304 | |||
| 998afb1ce0 | |||
| b362f86f9b | |||
| a5eba1789b | |||
| 48705e6670 | |||
| de2ef1dcc5 | |||
| 62a6eafdeb | |||
| ea276d35e9 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -5,6 +5,4 @@ out/
|
||||
.gradle
|
||||
build/
|
||||
|
||||
|
||||
!gradle-wrapper.jar
|
||||
gradle.properties
|
||||
36
CHANGELOG.md
36
CHANGELOG.md
@@ -0,0 +1,36 @@
|
||||
# Changelog
|
||||
|
||||
## [Unreleased]
|
||||
### Added
|
||||
- Server module
|
||||
- Change collector
|
||||
- Customizable accessors for colors
|
||||
- SphereLayer solid
|
||||
- Hexagon interface and GenericHexagon implementation (Box inherits Hexagon)
|
||||
- Increased the default detail level for spheres and cones to 32
|
||||
- Simple clipping for Solids in ThreeJs
|
||||
|
||||
### Changed
|
||||
- Vision does not implement ItemProvider anymore. Property changes are done via `getProperty`/`setProperty` and `property` delegate.
|
||||
- Point3D and Point2D are made separate classes instead of expect/actual (to split up different engines.
|
||||
- JavaFX support moved to a separate module
|
||||
- Threejs support moved to a separate module
|
||||
- \[Format breaking change!\] Stylesheets are moved into properties under `@stylesheet` key
|
||||
- VisionGroup builder accepts `null` as name for statics instead of `""`
|
||||
- gdml sphere is rendered as a SphereLayer instead of Sphere (#35)
|
||||
- Tube is replaced by more general ConeSurface
|
||||
- position, rotation and size moved to properties
|
||||
- prototypes moved to children
|
||||
- Immutable Solid instances
|
||||
- Property listeners are not triggered if there are no changes.
|
||||
- Feedback websocket connection in the client.
|
||||
|
||||
### Deprecated
|
||||
|
||||
### Removed
|
||||
- Primary modules dependencies on UI
|
||||
|
||||
### Fixed
|
||||
- Version conflicts
|
||||
|
||||
### Security
|
||||
|
||||
27
README.md
27
README.md
@@ -1,10 +1,16 @@
|
||||
[](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub)
|
||||
[](https://zenodo.org/badge/latestdoi/174502624)
|
||||
|
||||

|
||||
|
||||
[](https://kotlinlang.slack.com/archives/CEXV2QWNM)
|
||||
|
||||
# DataForge Visualization Platform
|
||||
|
||||
## Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Requirements](#requirements)
|
||||
* [Features](#features)
|
||||
* [About DataForge](#about-dataforge)
|
||||
* [Modules contained in this repository](#modules-contained-in-this-repository)
|
||||
@@ -29,6 +35,9 @@ Other applications including 2D plots are planned for the future.
|
||||
The project is developed as a [Kotlin multiplatform](https://kotlinlang.org/docs/reference/multiplatform.html)
|
||||
application, currently targeting browser JavaScript and JVM.
|
||||
|
||||
## Requirements
|
||||
|
||||
JVM backend requires JDK 11 or later
|
||||
|
||||
## Features
|
||||
|
||||
@@ -64,7 +73,7 @@ The `visionforge-core` module also includes configuration editors for JS (in `js
|
||||
|
||||
**Class diagram:**
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
### visionforge-solid
|
||||
@@ -73,7 +82,7 @@ Includes common classes and serializers for 3D visualization, as well as Three.j
|
||||
|
||||
**Class diagram:**
|
||||
|
||||

|
||||

|
||||
|
||||
##### Prototypes
|
||||
|
||||
@@ -82,7 +91,7 @@ also referred to as templates). The idea is that prototype geometry can be rende
|
||||
for multiple objects. This helps to significantly decrease memory usage.
|
||||
|
||||
The `prototypes` property tree is defined in `SolidGroup` class via `PrototypeHolder` interface, and
|
||||
`Proxy` class helps to reuse a template object.
|
||||
`SolidReference` class helps to reuse a template object.
|
||||
|
||||
##### Styles
|
||||
|
||||
@@ -118,7 +127,7 @@ Some shapes will also periodically change their color and visibility.
|
||||
|
||||
**Example view:**
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
### Full-Stack Application Example - Muon Monitor Visualization
|
||||
@@ -130,7 +139,7 @@ A full-stack application example, showing the
|
||||
|
||||
**Example view:**
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
### GDML Example
|
||||
@@ -141,4 +150,10 @@ Visualization example for geometry defined as GDML file.
|
||||
|
||||
##### Example view:
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
## Thanks and references
|
||||
The original three.js bindings were made by [Lars Ivar Hatledal](https://github.com/markaren), but the project is discontinued right now.
|
||||
|
||||
All other libraries are explicitly shown as dependencies. We would like to express specific thanks to JetBrains Kotlin-JS team for consulting us during the work.
|
||||
@@ -1,35 +1,41 @@
|
||||
import scientifik.useFx
|
||||
import scientifik.useSerialization
|
||||
|
||||
val dataforgeVersion by extra("0.1.8")
|
||||
|
||||
plugins {
|
||||
id("scientifik.mpp") apply false
|
||||
id("scientifik.jvm") apply false
|
||||
id("scientifik.js") apply false
|
||||
id("scientifik.publish") apply false
|
||||
id("org.jetbrains.changelog") version "0.4.0"
|
||||
id("ru.mipt.npm.gradle.project")
|
||||
|
||||
//Override kotlin version
|
||||
// val kotlinVersion = "1.5.20-RC"
|
||||
// kotlin("multiplatform") version(kotlinVersion) apply false
|
||||
// kotlin("jvm") version(kotlinVersion) apply false
|
||||
// kotlin("js") version(kotlinVersion) apply false
|
||||
}
|
||||
|
||||
val dataforgeVersion by extra("0.4.3")
|
||||
val fxVersion by extra("11")
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
maven("https://dl.bintray.com/pdvrieze/maven")
|
||||
maven("http://maven.jzy3d.org/releases")
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
maven("https://repo.kotlin.link")
|
||||
maven("https://maven.jzy3d.org/releases")
|
||||
}
|
||||
|
||||
group = "hep.dataforge"
|
||||
version = "0.1.5-dev"
|
||||
group = "space.kscience"
|
||||
version = "0.2.0-dev-22"
|
||||
}
|
||||
|
||||
val githubProject by extra("visionforge")
|
||||
val bintrayRepo by extra("dataforge")
|
||||
val fxVersion by extra("14")
|
||||
|
||||
subprojects {
|
||||
if(name.startsWith("visionforge")) {
|
||||
apply(plugin = "scientifik.publish")
|
||||
if (name.startsWith("visionforge")) {
|
||||
plugins.apply("maven-publish")
|
||||
}
|
||||
useSerialization()
|
||||
useFx(scientifik.FXModule.CONTROLS, version = fxVersion)
|
||||
}
|
||||
|
||||
ksciencePublish{
|
||||
github("visionforge")
|
||||
space()
|
||||
sonatype()
|
||||
}
|
||||
|
||||
apiValidation {
|
||||
validationDisabled = true
|
||||
ignoredPackages.add("info.laht.threekt")
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
import org.jetbrains.kotlin.gradle.frontend.KotlinFrontendExtension
|
||||
import org.jetbrains.kotlin.gradle.frontend.npm.NpmExtension
|
||||
import org.jetbrains.kotlin.gradle.frontend.webpack.WebPackExtension
|
||||
import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile
|
||||
|
||||
plugins {
|
||||
id("kotlin2js")
|
||||
id("kotlin-dce-js")
|
||||
id("org.jetbrains.kotlin.frontend")
|
||||
}
|
||||
|
||||
val kotlinVersion: String by rootProject.extra
|
||||
|
||||
dependencies {
|
||||
implementation(project(":visionforge-spatial-js"))
|
||||
testCompile(kotlin("test-js"))
|
||||
}
|
||||
|
||||
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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
package hep.dataforge.vis.jsroot
|
||||
|
||||
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
|
||||
@@ -1,6 +0,0 @@
|
||||
@file:JsModule("JSRootUtils")
|
||||
@file:JsNonModule
|
||||
|
||||
package hep.dataforge.vis.jsroot
|
||||
|
||||
external fun parse(obj: String): dynamic
|
||||
@@ -1,78 +0,0 @@
|
||||
package hep.dataforge.vis.jsroot
|
||||
|
||||
import hep.dataforge.context.Global
|
||||
import hep.dataforge.meta.EmptyMeta
|
||||
import hep.dataforge.vis.spatial.render
|
||||
import hep.dataforge.vis.spatial.three.ThreePlugin
|
||||
import hep.dataforge.vis.spatial.three.output
|
||||
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 kotlin.browser.document
|
||||
import kotlin.dom.clear
|
||||
|
||||
|
||||
class JSRootDemoApp : ApplicationBase() {
|
||||
|
||||
override val stateKeys: List<String> = emptyList()
|
||||
|
||||
override fun start(state: Map<String, Any>) {
|
||||
|
||||
|
||||
//TODO remove after DI fix
|
||||
// Global.plugins.load(ThreePlugin())
|
||||
// Global.plugins.load(JSRootPlugin())
|
||||
|
||||
Global.plugins.load(JSRootPlugin)
|
||||
|
||||
|
||||
|
||||
(document.getElementById("drop_zone") as? HTMLDivElement)?.apply {
|
||||
addEventListener("dragover", { handleDragOver(it) }, false)
|
||||
addEventListener("drop", { loadData(it) }, false)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
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
|
||||
val renderer = Global.plugins.fetch(ThreePlugin).output()
|
||||
val canvas = document.getElementById("canvas")!!
|
||||
canvas.clear()
|
||||
renderer.attach(canvas)
|
||||
println("started")
|
||||
|
||||
renderer.render {
|
||||
val json = parse(string)
|
||||
JSRootObject(this, EmptyMeta, json).also { add(it) }
|
||||
}
|
||||
}
|
||||
readAsText(file)
|
||||
}
|
||||
}
|
||||
|
||||
override fun dispose() = emptyMap<String, Any>()//mapOf("lines" put presenter.dispose())
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
@file:JsModule("JSRootGeoBase")
|
||||
@file:JsNonModule
|
||||
|
||||
package hep.dataforge.vis.jsroot
|
||||
|
||||
import info.laht.threekt.core.BufferGeometry
|
||||
import info.laht.threekt.core.Object3D
|
||||
|
||||
external fun createGeometry(shape: dynamic, limit: Int): BufferGeometry
|
||||
|
||||
external fun createCubeBuffer(shape: dynamic, limit: Int): BufferGeometry
|
||||
|
||||
external fun createTubeBuffer(shape: dynamic, limit: Int): BufferGeometry
|
||||
|
||||
external fun createXtruBuffer(shape: dynamic, limit: Int): BufferGeometry
|
||||
|
||||
external fun build(obj: dynamic, opt: dynamic): Object3D
|
||||
@@ -1,82 +0,0 @@
|
||||
package hep.dataforge.vis.jsroot
|
||||
|
||||
import hep.dataforge.meta.EmptyMeta
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.buildMeta
|
||||
import hep.dataforge.meta.toDynamic
|
||||
import hep.dataforge.vis.common.*
|
||||
import hep.dataforge.vis.spatial.three.MeshThreeFactory
|
||||
import info.laht.threekt.core.BufferGeometry
|
||||
|
||||
class JSRootGeometry(parent: VisualObject?, meta: Meta) : DisplayLeaf(parent, meta) {
|
||||
|
||||
var shape by node()
|
||||
|
||||
var facesLimit by int(0)
|
||||
|
||||
fun box(xSize: Number, ySize: Number, zSize: Number) = buildMeta {
|
||||
"_typename" put "TGeoBBox"
|
||||
"fDX" put xSize
|
||||
"fDY" put ySize
|
||||
"fDZ" put zSize
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a GDML union
|
||||
*/
|
||||
operator fun Meta.plus(other: Meta) = buildMeta {
|
||||
"fNode.fLeft" put this
|
||||
"fNode.fRight" put other
|
||||
"fNode._typename" put "TGeoUnion"
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a GDML subtraction
|
||||
*/
|
||||
operator fun Meta.minus(other: Meta) = buildMeta {
|
||||
"fNode.fLeft" put this
|
||||
"fNode.fRight" put other
|
||||
"fNode._typename" put "TGeoSubtraction"
|
||||
}
|
||||
|
||||
/**
|
||||
* Intersect two GDML geometries
|
||||
*/
|
||||
infix fun Meta.intersect(other: Meta) = buildMeta {
|
||||
"fNode.fLeft" put this
|
||||
"fNode.fRight" put other
|
||||
"fNode._typename" put "TGeoIntersection"
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TYPE = "geometry.spatial.jsRoot.geometry"
|
||||
}
|
||||
}
|
||||
|
||||
fun VisualGroup.jsRootGeometry(meta: Meta = EmptyMeta, action: JSRootGeometry.() -> Unit = {}) =
|
||||
JSRootGeometry(this, meta).apply(action).also { add(it) }
|
||||
|
||||
//fun Meta.toDynamic(): dynamic {
|
||||
// fun MetaItem<*>.toDynamic(): dynamic = when (this) {
|
||||
// is MetaItem.ValueItem -> this.value.value.asDynamic()
|
||||
// is MetaItem.NodeItem -> this.node.toDynamic()
|
||||
// }
|
||||
//
|
||||
// val res = js("{}")
|
||||
// this.items.entries.groupBy { it.key.body }.forEach { (key, value) ->
|
||||
// val list = value.map { it.value }
|
||||
// res[key] = when (list.size) {
|
||||
// 1 -> list.first().toDynamic()
|
||||
// else -> list.map { it.toDynamic() }
|
||||
// }
|
||||
// }
|
||||
// return res
|
||||
//}
|
||||
|
||||
|
||||
object ThreeJSRootGeometryFactory : MeshThreeFactory<JSRootGeometry>(JSRootGeometry::class) {
|
||||
override fun buildGeometry(obj: JSRootGeometry): BufferGeometry {
|
||||
val shapeMeta = obj.shape?.toDynamic() ?: error("The shape not defined")
|
||||
return createGeometry(shapeMeta, obj.facesLimit)
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package hep.dataforge.vis.jsroot
|
||||
|
||||
import hep.dataforge.meta.EmptyMeta
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.toDynamic
|
||||
import hep.dataforge.vis.common.VisualGroup
|
||||
import hep.dataforge.vis.common.DisplayLeaf
|
||||
import hep.dataforge.vis.common.VisualObject
|
||||
import hep.dataforge.vis.common.node
|
||||
import hep.dataforge.vis.spatial.three.ThreeFactory
|
||||
import info.laht.threekt.core.Object3D
|
||||
|
||||
class JSRootObject(parent: VisualObject?, meta: Meta, val data: dynamic) : DisplayLeaf(parent, meta) {
|
||||
|
||||
var options by node()
|
||||
|
||||
companion object {
|
||||
const val TYPE = "geometry.spatial.jsRoot.object"
|
||||
}
|
||||
}
|
||||
|
||||
object ThreeJSRootObjectFactory : ThreeFactory<JSRootObject> {
|
||||
|
||||
override val type = JSRootObject::class
|
||||
|
||||
override fun invoke(obj: JSRootObject): Object3D {
|
||||
return build(obj.data, obj.options?.toDynamic())
|
||||
}
|
||||
}
|
||||
|
||||
fun VisualGroup.jsRootObject(str: String) {
|
||||
val json = JSON.parse<Any>(str)
|
||||
JSRootObject(this, EmptyMeta, json).also { add(it) }
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package hep.dataforge.vis.jsroot
|
||||
|
||||
import hep.dataforge.context.AbstractPlugin
|
||||
import hep.dataforge.context.PluginFactory
|
||||
import hep.dataforge.context.PluginTag
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.toName
|
||||
import hep.dataforge.vis.spatial.three.ThreeFactory
|
||||
import hep.dataforge.vis.spatial.three.ThreePlugin
|
||||
|
||||
class JSRootPlugin : AbstractPlugin() {
|
||||
override val tag: PluginTag get() = Companion.tag
|
||||
|
||||
override fun dependsOn() = listOf(ThreePlugin)
|
||||
|
||||
override fun provideTop(target: String): Map<Name, Any> {
|
||||
return when(target){
|
||||
ThreeFactory.TYPE -> mapOf(
|
||||
"jsRoot.geometry".toName() to ThreeJSRootGeometryFactory,
|
||||
"jsRoot.object".toName() to ThreeJSRootObjectFactory
|
||||
)
|
||||
else -> emptyMap()
|
||||
}
|
||||
}
|
||||
|
||||
companion object: PluginFactory<JSRootPlugin> {
|
||||
override val tag = PluginTag("vis.jsroot", "hep.dataforge")
|
||||
override val type = JSRootPlugin::class
|
||||
override fun invoke(meta: Meta) = JSRootPlugin()
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package hep.dataforge.vis.jsroot
|
||||
|
||||
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("application") == true) {
|
||||
val application = JSRootDemoApp()
|
||||
|
||||
@Suppress("UnsafeCastFromDynamic")
|
||||
application.start(state?.appState ?: emptyMap<String, Any>())
|
||||
|
||||
application
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
3577
dataforge-vis-jsroot/src/main/resources/JSRootGeoBase.js
vendored
3577
dataforge-vis-jsroot/src/main/resources/JSRootGeoBase.js
vendored
File diff suppressed because it is too large
Load Diff
@@ -1,765 +0,0 @@
|
||||
|
||||
/** Generate mask for given bit
|
||||
*
|
||||
* @param {number} n bit number
|
||||
* @returns {Number} produced make
|
||||
* @private */
|
||||
export function BIT(n) {
|
||||
return 1 << (n);
|
||||
}
|
||||
|
||||
/** Wrapper for console.log, let redirect output to specified div element
|
||||
* @private */
|
||||
function console(value, divid) {
|
||||
if ((typeof divid == 'string') && document.getElementById(divid))
|
||||
document.getElementById(divid).innerHTML = value;
|
||||
else if ((typeof console != 'undefined') && (typeof console.log == 'function'))
|
||||
console.log(value);
|
||||
}
|
||||
|
||||
|
||||
/** @summary Wrapper for alert, throws Error in Node.js
|
||||
* @private */
|
||||
export function alert(msg) {
|
||||
if (this.nodeis) throw new Error(msg);
|
||||
if (typeof alert === 'function') alert(msg);
|
||||
else console('ALERT: ' + msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Seed simple random generator
|
||||
*
|
||||
* @private
|
||||
* @param {number} i seed value
|
||||
*/
|
||||
export function seed(i) {
|
||||
i = Math.abs(i);
|
||||
if (i > 1e8) i = Math.abs(1e8 * Math.sin(i)); else if (i < 1) i *= 1e8;
|
||||
this.m_w = Math.round(i);
|
||||
this.m_z = 987654321;
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Simple random generator
|
||||
*
|
||||
* @desc Works like Math.random(), but with configurable seed - see {@link JSROOT.seed}
|
||||
* @private
|
||||
* @returns {number} random value between 0 (inclusive) and 1.0 (exclusive)
|
||||
*/
|
||||
export function random() {
|
||||
if (this.m_z === undefined) return Math.random();
|
||||
this.m_z = (36969 * (this.m_z & 65535) + (this.m_z >> 16)) & 0xffffffff;
|
||||
this.m_w = (18000 * (this.m_w & 65535) + (this.m_w >> 16)) & 0xffffffff;
|
||||
var result = ((this.m_z << 16) + this.m_w) & 0xffffffff;
|
||||
result /= 4294967296;
|
||||
return result + 0.5;
|
||||
}
|
||||
|
||||
|
||||
/** @summary Should be used to reintroduce objects references, produced by TBufferJSON.
|
||||
*
|
||||
* @desc Replace all references inside object, object should not be null
|
||||
* Idea of the code taken from JSON-R code, found on
|
||||
* https://github.com/graniteds/jsonr
|
||||
* Only unref part was used, arrays are not accounted as objects
|
||||
* @param {object} obj object where references will be replaced
|
||||
* @returns {object} same object with replaced references
|
||||
* @private */
|
||||
function JSONR_unref(obj) {
|
||||
|
||||
let map = [], newfmt = undefined;
|
||||
|
||||
function unref_value(value) {
|
||||
if ((value === null) || (value === undefined)) return;
|
||||
|
||||
/*
|
||||
if object is a reference string in "old format"
|
||||
Old format seems to be single string with "$ref:" prefix. New format is an object
|
||||
*/
|
||||
if (typeof value === 'string') {
|
||||
if (newfmt || (value.length < 6) || (value.indexOf("$ref:") !== 0)) return; //switch to "new format" if needed
|
||||
let ref = parseInt(value.substr(5)); // get ref number
|
||||
if (isNaN(ref) || (ref < 0) || (ref >= map.length)) return; //skip if not a ref
|
||||
newfmt = false;
|
||||
return map[ref]; //return an object from cache
|
||||
}
|
||||
|
||||
if (typeof value !== 'object') return;
|
||||
|
||||
let i, k, res, proto = Object.prototype.toString.apply(value);
|
||||
|
||||
// scan array - it can contain other objects
|
||||
if ((proto.indexOf('[object') === 0) && (proto.indexOf('Array]') > 0)) {
|
||||
for (i = 0; i < value.length; ++i) {
|
||||
res = unref_value(value[i]);
|
||||
if (res !== undefined) value[i] = res;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let ks = Object.keys(value), len = ks.length;
|
||||
|
||||
if ((newfmt !== false) && (len === 1) && (ks[0] === '$ref')) {
|
||||
let ref = parseInt(value['$ref']);
|
||||
if (isNaN(ref) || (ref < 0) || (ref >= map.length)) return;
|
||||
newfmt = true;
|
||||
return map[ref];
|
||||
}
|
||||
|
||||
if ((newfmt !== false) && (len > 1) && (ks[0] === '$arr') && (ks[1] === 'len')) {
|
||||
// this is ROOT-coded array
|
||||
var arr = null, dflt = (value.$arr === "Bool") ? false : 0;
|
||||
switch (value.$arr) {
|
||||
case "Int8" :
|
||||
arr = new Int8Array(value.len);
|
||||
break;
|
||||
case "Uint8" :
|
||||
arr = new Uint8Array(value.len);
|
||||
break;
|
||||
case "Int16" :
|
||||
arr = new Int16Array(value.len);
|
||||
break;
|
||||
case "Uint16" :
|
||||
arr = new Uint16Array(value.len);
|
||||
break;
|
||||
case "Int32" :
|
||||
arr = new Int32Array(value.len);
|
||||
break;
|
||||
case "Uint32" :
|
||||
arr = new Uint32Array(value.len);
|
||||
break;
|
||||
case "Float32" :
|
||||
arr = new Float32Array(value.len);
|
||||
break;
|
||||
case "Int64" :
|
||||
case "Uint64" :
|
||||
case "Float64" :
|
||||
arr = new Float64Array(value.len);
|
||||
break;
|
||||
default :
|
||||
arr = new Array(value.len);
|
||||
break;
|
||||
}
|
||||
for (let k = 0; k < value.len; ++k) arr[k] = dflt;
|
||||
|
||||
var nkey = 2, p = 0;
|
||||
while (nkey < len) {
|
||||
if (ks[nkey][0] === "p") p = value[ks[nkey++]]; // position
|
||||
if (ks[nkey][0] !== 'v') throw new Error('Unexpected member ' + ks[nkey] + ' in array decoding');
|
||||
let v = value[ks[nkey++]]; // value
|
||||
if (typeof v === 'object') {
|
||||
for (let k = 0; k < v.length; ++k) arr[p++] = v[k];
|
||||
} else {
|
||||
arr[p++] = v;
|
||||
if ((nkey < len) && (ks[nkey][0] === 'n')) {
|
||||
let cnt = value[ks[nkey++]]; // counter
|
||||
while (--cnt) arr[p++] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
if ((newfmt !== false) && (len === 3) && (ks[0] === '$pair') && (ks[1] === 'first') && (ks[2] === 'second')) {
|
||||
newfmt = true;
|
||||
let f1 = unref_value(value.first),
|
||||
s1 = unref_value(value.second);
|
||||
if (f1 !== undefined) value.first = f1;
|
||||
if (s1 !== undefined) value.second = s1;
|
||||
value._typename = value['$pair'];
|
||||
delete value['$pair'];
|
||||
return; // pair object is not counted in the objects map
|
||||
}
|
||||
|
||||
// debug code, can be commented out later
|
||||
if (map.indexOf(value) >= 0) {
|
||||
console('should never happen - object already in the map');
|
||||
return;
|
||||
}
|
||||
|
||||
// add object to object map
|
||||
map.push(value);
|
||||
|
||||
// add methods to all objects, where _typename is specified
|
||||
//if ('_typename' in value) JSROOT.addMethods(value);
|
||||
|
||||
for (let k = 0; k < len; ++k) {
|
||||
i = ks[k];
|
||||
res = unref_value(value[i]);
|
||||
if (res !== undefined) value[i] = res;
|
||||
}
|
||||
}
|
||||
|
||||
unref_value(obj);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
/** @summary Just copies (not clone) all fields from source to the target object
|
||||
* @desc This is simple replacement of jQuery.extend method
|
||||
* @private */
|
||||
function extend(tgt, src) {
|
||||
if ((src === null) || (typeof src !== 'object')) return tgt;
|
||||
if ((tgt === null) || (typeof tgt !== 'object')) tgt = {};
|
||||
|
||||
for (var k in src)
|
||||
tgt[k] = src[k];
|
||||
|
||||
return tgt;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @summary Parse JSON code produced with TBufferJSON.
|
||||
*
|
||||
* @param {string} json string to parse
|
||||
* @return {object|null} returns parsed object
|
||||
*/
|
||||
export function parse(json) {
|
||||
if (!json) return null;
|
||||
let obj = JSON.parse(json);
|
||||
if (obj) obj = JSONR_unref(obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @summary Parse multi.json request results
|
||||
* @desc Method should be used to parse JSON code, produced by multi.json request of THttpServer
|
||||
*
|
||||
* @param {string} json string to parse
|
||||
* @return {Array|null} returns array of parsed elements
|
||||
*/
|
||||
export function parse_multi(json) {
|
||||
if (!json) return null;
|
||||
let arr = JSON.parse(json);
|
||||
if (arr && arr.length)
|
||||
for (let i = 0; i < arr.length; ++i)
|
||||
arr[i] = JSONR_unref(arr[i]);
|
||||
return arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Method converts JavaScript object into ROOT-like JSON
|
||||
*
|
||||
* @desc Produced JSON can be used in JSROOT.parse() again
|
||||
* When performed properly, JSON can be used in TBufferJSON to read data back with C++
|
||||
*/
|
||||
export function toJSON(obj) {
|
||||
if (!obj || typeof obj !== 'object') return "";
|
||||
|
||||
var map = []; // map of stored objects
|
||||
|
||||
function copy_value(value) {
|
||||
if (typeof value === "function") return undefined;
|
||||
|
||||
if ((value === undefined) || (value === null) || (typeof value !== 'object')) return value;
|
||||
|
||||
var proto = Object.prototype.toString.apply(value);
|
||||
|
||||
// typed array need to be converted into normal array, otherwise looks strange
|
||||
if ((proto.indexOf('[object ') === 0) && (proto.indexOf('Array]') === proto.length - 6)) {
|
||||
var arr = new Array(value.length);
|
||||
for (var i = 0; i < value.length; ++i)
|
||||
arr[i] = copy_value(value[i]);
|
||||
return arr;
|
||||
}
|
||||
|
||||
// this is how reference is code
|
||||
var refid = map.indexOf(value);
|
||||
if (refid >= 0) return {$ref: refid};
|
||||
|
||||
var ks = Object.keys(value), len = ks.length, tgt = {};
|
||||
|
||||
if ((len === 3) && (ks[0] === '$pair') && (ks[1] === 'first') && (ks[2] === 'second')) {
|
||||
// special handling of pair objects which does not included into objects map
|
||||
tgt.$pair = value.$pair;
|
||||
tgt.first = copy_value(value.first);
|
||||
tgt.second = copy_value(value.second);
|
||||
return tgt;
|
||||
}
|
||||
|
||||
map.push(value);
|
||||
|
||||
for (var k = 0; k < len; ++k) {
|
||||
var name = ks[k];
|
||||
tgt[name] = copy_value(value[name]);
|
||||
}
|
||||
|
||||
return tgt;
|
||||
}
|
||||
|
||||
var tgt = copy_value(obj);
|
||||
|
||||
return JSON.stringify(tgt);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @summary Parse string value as array.
|
||||
*
|
||||
* @desc It could be just simple string: "value" or
|
||||
* array with or without string quotes: [element], ['elem1',elem2]
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function ParseAsArray(val) {
|
||||
|
||||
var res = [];
|
||||
|
||||
if (typeof val != 'string') return res;
|
||||
|
||||
val = val.trim();
|
||||
if (val === "") return res;
|
||||
|
||||
// return as array with single element
|
||||
if ((val.length < 2) || (val[0] !== '[') || (val[val.length - 1] !== ']')) {
|
||||
res.push(val);
|
||||
return res;
|
||||
}
|
||||
|
||||
// try to split ourself, checking quotes and brackets
|
||||
var nbr = 0, nquotes = 0, ndouble = 0, last = 1;
|
||||
|
||||
for (var indx = 1; indx < val.length; ++indx) {
|
||||
if (nquotes > 0) {
|
||||
if (val[indx] === "'") nquotes--;
|
||||
continue;
|
||||
}
|
||||
if (ndouble > 0) {
|
||||
if (val[indx] === '"') ndouble--;
|
||||
continue;
|
||||
}
|
||||
switch (val[indx]) {
|
||||
case "'":
|
||||
nquotes++;
|
||||
break;
|
||||
case '"':
|
||||
ndouble++;
|
||||
break;
|
||||
case "[":
|
||||
nbr++;
|
||||
break;
|
||||
case "]":
|
||||
if (indx < val.length - 1) {
|
||||
nbr--;
|
||||
break;
|
||||
}
|
||||
case ",":
|
||||
if (nbr === 0) {
|
||||
var sub = val.substring(last, indx).trim();
|
||||
if ((sub.length > 1) && (sub[0] === sub[sub.length - 1]) && ((sub[0] === '"') || (sub[0] === "'")))
|
||||
sub = sub.substr(1, sub.length - 2);
|
||||
res.push(sub);
|
||||
last = indx + 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (res.length === 0)
|
||||
res.push(val.substr(1, val.length - 2).trim());
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Find function with given name.
|
||||
*
|
||||
* @desc Function name may include several namespaces like 'JSROOT.Painter.drawFrame'
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function findFunction(name) {
|
||||
if (typeof name === 'function') return name;
|
||||
if (typeof name !== 'string') return null;
|
||||
var names = name.split('.'), elem = null;
|
||||
if (typeof window === 'object') elem = window;
|
||||
if (names[0] === 'JSROOT') {
|
||||
elem = this;
|
||||
names.shift();
|
||||
}
|
||||
|
||||
for (var n = 0; elem && (n < names.length); ++n)
|
||||
elem = elem[names[n]];
|
||||
|
||||
return (typeof elem == 'function') ? elem : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Generic method to invoke callback function.
|
||||
*
|
||||
* @param {object|function} func either normal function or container like
|
||||
* { obj: object_pointer, func: name of method to call }
|
||||
* @param arg1 first optional argument of callback
|
||||
* @param arg2 second optional argument of callback
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function callBack(func, arg1, arg2) {
|
||||
|
||||
if (typeof func == 'string') func = findFunction(func);
|
||||
|
||||
if (!func) return;
|
||||
|
||||
if (typeof func == 'function') return func(arg1, arg2);
|
||||
|
||||
if (typeof func != 'object') return;
|
||||
|
||||
if (('obj' in func) && ('func' in func) &&
|
||||
(typeof func.obj == 'object') && (typeof func.func == 'string') &&
|
||||
(typeof func.obj[func.func] == 'function')) {
|
||||
return func.obj[func.func](arg1, arg2);
|
||||
}
|
||||
}
|
||||
|
||||
let methodsCache = {}; // variable used to keep methods for known classes
|
||||
|
||||
|
||||
/** @summary Returns methods for given typename
|
||||
* @private
|
||||
*/
|
||||
function getMethods(typename, obj) {
|
||||
|
||||
var m = methodsCache[typename],
|
||||
has_methods = (m !== undefined);
|
||||
|
||||
if (!has_methods) m = {};
|
||||
|
||||
// Due to binary I/O such TObject methods may not be set for derived classes
|
||||
// Therefore when methods requested for given object, check also that basic methods are there
|
||||
if ((typename === "TObject") || (typename === "TNamed") || (obj && (obj.fBits !== undefined)))
|
||||
if (m.TestBit === undefined) {
|
||||
m.TestBit = function (f) {
|
||||
return (this.fBits & f) !== 0;
|
||||
};
|
||||
m.InvertBit = function (f) {
|
||||
this.fBits = this.fBits ^ (f & 0xffffff);
|
||||
};
|
||||
}
|
||||
|
||||
if (has_methods) return m;
|
||||
|
||||
if ((typename === 'TList') || (typename === 'THashList')) {
|
||||
m.Clear = function () {
|
||||
this.arr = [];
|
||||
this.opt = [];
|
||||
};
|
||||
m.Add = function (obj, opt) {
|
||||
this.arr.push(obj);
|
||||
this.opt.push((opt && typeof opt == 'string') ? opt : "");
|
||||
};
|
||||
m.AddFirst = function (obj, opt) {
|
||||
this.arr.unshift(obj);
|
||||
this.opt.unshift((opt && typeof opt == 'string') ? opt : "");
|
||||
};
|
||||
m.RemoveAt = function (indx) {
|
||||
this.arr.splice(indx, 1);
|
||||
this.opt.splice(indx, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// if ((typename === "TPaveText") || (typename === "TPaveStats")) {
|
||||
// m.AddText = function (txt) {
|
||||
// // this.fLines.Add({ _typename: 'TLatex', fTitle: txt, fTextColor: 1 });
|
||||
// var line = JSROOT.Create("TLatex");
|
||||
// line.fTitle = txt;
|
||||
// this.fLines.Add(line);
|
||||
// };
|
||||
// m.Clear = function () {
|
||||
// this.fLines.Clear();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if ((typename.indexOf("TF1") === 0) || (typename === "TF2")) {
|
||||
// m.addFormula = function (obj) {
|
||||
// if (!obj) return;
|
||||
// if (this.formulas === undefined) this.formulas = [];
|
||||
// this.formulas.push(obj);
|
||||
// };
|
||||
//
|
||||
// m.evalPar = function (x, y) {
|
||||
// if (!('_func' in this) || (this._title !== this.fTitle)) {
|
||||
//
|
||||
// var _func = this.fTitle, isformula = false, pprefix = "[";
|
||||
// if (_func === "gaus") _func = "gaus(0)";
|
||||
// if (this.fFormula && typeof this.fFormula.fFormula == "string") {
|
||||
// if (this.fFormula.fFormula.indexOf("[](double*x,double*p)") === 0) {
|
||||
// isformula = true;
|
||||
// pprefix = "p[";
|
||||
// _func = this.fFormula.fFormula.substr(21);
|
||||
// } else {
|
||||
// _func = this.fFormula.fFormula;
|
||||
// pprefix = "[p";
|
||||
// }
|
||||
// if (this.fFormula.fClingParameters && this.fFormula.fParams) {
|
||||
// for (var i = 0; i < this.fFormula.fParams.length; ++i) {
|
||||
// var regex = new RegExp('(\\[' + this.fFormula.fParams[i].first + '\\])', 'g'),
|
||||
// parvalue = this.fFormula.fClingParameters[this.fFormula.fParams[i].second];
|
||||
// _func = _func.replace(regex, (parvalue < 0) ? "(" + parvalue + ")" : parvalue);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if ('formulas' in this)
|
||||
// for (var i = 0; i < this.formulas.length; ++i)
|
||||
// while (_func.indexOf(this.formulas[i].fName) >= 0)
|
||||
// _func = _func.replace(this.formulas[i].fName, this.formulas[i].fTitle);
|
||||
// _func = _func.replace(/\b(abs)\b/g, 'TMath::Abs')
|
||||
// .replace(/TMath::Exp\(/g, 'Math.exp(')
|
||||
// .replace(/TMath::Abs\(/g, 'Math.abs(');
|
||||
// if (typeof JSROOT.Math == 'object') {
|
||||
// this._math = JSROOT.Math;
|
||||
// _func = _func.replace(/TMath::Prob\(/g, 'this._math.Prob(')
|
||||
// .replace(/TMath::Gaus\(/g, 'this._math.Gaus(')
|
||||
// .replace(/TMath::BreitWigner\(/g, 'this._math.BreitWigner(')
|
||||
// .replace(/xygaus\(/g, 'this._math.gausxy(this, x, y, ')
|
||||
// .replace(/gaus\(/g, 'this._math.gaus(this, x, ')
|
||||
// .replace(/gausn\(/g, 'this._math.gausn(this, x, ')
|
||||
// .replace(/expo\(/g, 'this._math.expo(this, x, ')
|
||||
// .replace(/landau\(/g, 'this._math.landau(this, x, ')
|
||||
// .replace(/landaun\(/g, 'this._math.landaun(this, x, ')
|
||||
// .replace(/ROOT::Math::/g, 'this._math.');
|
||||
// }
|
||||
// for (var i = 0; i < this.fNpar; ++i) {
|
||||
// var parname = pprefix + i + "]";
|
||||
// while (_func.indexOf(parname) !== -1)
|
||||
// _func = _func.replace(parname, '(' + this.GetParValue(i) + ')');
|
||||
// }
|
||||
// _func = _func.replace(/\b(sin)\b/gi, 'Math.sin')
|
||||
// .replace(/\b(cos)\b/gi, 'Math.cos')
|
||||
// .replace(/\b(tan)\b/gi, 'Math.tan')
|
||||
// .replace(/\b(exp)\b/gi, 'Math.exp')
|
||||
// .replace(/\b(pow)\b/gi, 'Math.pow')
|
||||
// .replace(/pi/g, 'Math.PI');
|
||||
// for (var n = 2; n < 10; ++n)
|
||||
// _func = _func.replace('x^' + n, 'Math.pow(x,' + n + ')');
|
||||
//
|
||||
// if (isformula) {
|
||||
// _func = _func.replace(/x\[0\]/g, "x");
|
||||
// if (this._typename === "TF2") {
|
||||
// _func = _func.replace(/x\[1\]/g, "y");
|
||||
// this._func = new Function("x", "y", _func).bind(this);
|
||||
// } else {
|
||||
// this._func = new Function("x", _func).bind(this);
|
||||
// }
|
||||
// } else if (this._typename === "TF2")
|
||||
// this._func = new Function("x", "y", "return " + _func).bind(this);
|
||||
// else
|
||||
// this._func = new Function("x", "return " + _func).bind(this);
|
||||
//
|
||||
// this._title = this.fTitle;
|
||||
// }
|
||||
//
|
||||
// return this._func(x, y);
|
||||
// };
|
||||
// m.GetParName = function (n) {
|
||||
// if (this.fFormula && this.fFormula.fParams) return this.fFormula.fParams[n].first;
|
||||
// if (this.fNames && this.fNames[n]) return this.fNames[n];
|
||||
// return "p" + n;
|
||||
// };
|
||||
// m.GetParValue = function (n) {
|
||||
// if (this.fFormula && this.fFormula.fClingParameters) return this.fFormula.fClingParameters[n];
|
||||
// if (this.fParams) return this.fParams[n];
|
||||
// return undefined;
|
||||
// };
|
||||
// m.GetParError = function (n) {
|
||||
// return this.fParErrors ? this.fParErrors[n] : undefined;
|
||||
// };
|
||||
// m.GetNumPars = function () {
|
||||
// return this.fNpar;
|
||||
// }
|
||||
// }
|
||||
|
||||
if (((typename.indexOf("TGraph") === 0) || (typename === "TCutG")) && (typename !== "TGraphPolargram") && (typename !== "TGraphTime")) {
|
||||
// check if point inside figure specified by the TGraph
|
||||
m.IsInside = function (xp, yp) {
|
||||
var i, j = this.fNpoints - 1, x = this.fX, y = this.fY, oddNodes = false;
|
||||
|
||||
for (i = 0; i < this.fNpoints; ++i) {
|
||||
if ((y[i] < yp && y[j] >= yp) || (y[j] < yp && y[i] >= yp)) {
|
||||
if (x[i] + (yp - y[i]) / (y[j] - y[i]) * (x[j] - x[i]) < xp) {
|
||||
oddNodes = !oddNodes;
|
||||
}
|
||||
}
|
||||
j = i;
|
||||
}
|
||||
|
||||
return oddNodes;
|
||||
};
|
||||
}
|
||||
|
||||
if (typename.indexOf("TH1") === 0 ||
|
||||
typename.indexOf("TH2") === 0 ||
|
||||
typename.indexOf("TH3") === 0) {
|
||||
m.getBinError = function (bin) {
|
||||
// -*-*-*-*-*Return value of error associated to bin number bin*-*-*-*-*
|
||||
// if the sum of squares of weights has been defined (via Sumw2),
|
||||
// this function returns the sqrt(sum of w2).
|
||||
// otherwise it returns the sqrt(contents) for this bin.
|
||||
if (bin >= this.fNcells) bin = this.fNcells - 1;
|
||||
if (bin < 0) bin = 0;
|
||||
if (bin < this.fSumw2.length)
|
||||
return Math.sqrt(this.fSumw2[bin]);
|
||||
return Math.sqrt(Math.abs(this.fArray[bin]));
|
||||
};
|
||||
m.setBinContent = function (bin, content) {
|
||||
// Set bin content - only trivial case, without expansion
|
||||
this.fEntries++;
|
||||
this.fTsumw = 0;
|
||||
if ((bin >= 0) && (bin < this.fArray.length))
|
||||
this.fArray[bin] = content;
|
||||
};
|
||||
}
|
||||
|
||||
if (typename.indexOf("TH1") === 0) {
|
||||
m.getBin = function (x) {
|
||||
return x;
|
||||
};
|
||||
m.getBinContent = function (bin) {
|
||||
return this.fArray[bin];
|
||||
};
|
||||
m.Fill = function (x, weight) {
|
||||
var axis = this.fXaxis,
|
||||
bin = 1 + Math.floor((x - axis.fXmin) / (axis.fXmax - axis.fXmin) * axis.fNbins);
|
||||
if (bin < 0) bin = 0; else if (bin > axis.fNbins + 1) bin = axis.fNbins + 1;
|
||||
this.fArray[bin] += ((weight === undefined) ? 1 : weight);
|
||||
}
|
||||
}
|
||||
|
||||
if (typename.indexOf("TH2") === 0) {
|
||||
m.getBin = function (x, y) {
|
||||
return (x + (this.fXaxis.fNbins + 2) * y);
|
||||
};
|
||||
m.getBinContent = function (x, y) {
|
||||
return this.fArray[this.getBin(x, y)];
|
||||
};
|
||||
m.Fill = function (x, y, weight) {
|
||||
var axis1 = this.fXaxis, axis2 = this.fYaxis,
|
||||
bin1 = 1 + Math.floor((x - axis1.fXmin) / (axis1.fXmax - axis1.fXmin) * axis1.fNbins),
|
||||
bin2 = 1 + Math.floor((y - axis2.fXmin) / (axis2.fXmax - axis2.fXmin) * axis2.fNbins);
|
||||
if (bin1 < 0) bin1 = 0; else if (bin1 > axis1.fNbins + 1) bin1 = axis1.fNbins + 1;
|
||||
if (bin2 < 0) bin2 = 0; else if (bin2 > axis2.fNbins + 1) bin2 = axis2.fNbins + 1;
|
||||
this.fArray[bin1 + (axis1.fNbins + 2) * bin2] += ((weight === undefined) ? 1 : weight);
|
||||
}
|
||||
}
|
||||
|
||||
if (typename.indexOf("TH3") === 0) {
|
||||
m.getBin = function (x, y, z) {
|
||||
return (x + (this.fXaxis.fNbins + 2) * (y + (this.fYaxis.fNbins + 2) * z));
|
||||
};
|
||||
m.getBinContent = function (x, y, z) {
|
||||
return this.fArray[this.getBin(x, y, z)];
|
||||
};
|
||||
m.Fill = function (x, y, z, weight) {
|
||||
var axis1 = this.fXaxis, axis2 = this.fYaxis, axis3 = this.fZaxis,
|
||||
bin1 = 1 + Math.floor((x - axis1.fXmin) / (axis1.fXmax - axis1.fXmin) * axis1.fNbins),
|
||||
bin2 = 1 + Math.floor((y - axis2.fXmin) / (axis2.fXmax - axis2.fXmin) * axis2.fNbins),
|
||||
bin3 = 1 + Math.floor((z - axis3.fXmin) / (axis3.fXmax - axis3.fXmin) * axis3.fNbins);
|
||||
if (bin1 < 0) bin1 = 0; else if (bin1 > axis1.fNbins + 1) bin1 = axis1.fNbins + 1;
|
||||
if (bin2 < 0) bin2 = 0; else if (bin2 > axis2.fNbins + 1) bin2 = axis2.fNbins + 1;
|
||||
if (bin3 < 0) bin3 = 0; else if (bin3 > axis3.fNbins + 1) bin3 = axis3.fNbins + 1;
|
||||
this.fArray[bin1 + (axis1.fNbins + 2) * (bin2 + (axis2.fNbins + 2) * bin3)] += ((weight === undefined) ? 1 : weight);
|
||||
}
|
||||
}
|
||||
|
||||
if (typename.indexOf("TProfile") === 0) {
|
||||
if (typename.indexOf("TProfile2D") === 0) {
|
||||
m.getBin = function (x, y) {
|
||||
return (x + (this.fXaxis.fNbins + 2) * y);
|
||||
};
|
||||
m.getBinContent = function (x, y) {
|
||||
var bin = this.getBin(x, y);
|
||||
if (bin < 0 || bin >= this.fNcells) return 0;
|
||||
if (this.fBinEntries[bin] < 1e-300) return 0;
|
||||
if (!this.fArray) return 0;
|
||||
return this.fArray[bin] / this.fBinEntries[bin];
|
||||
};
|
||||
m.getBinEntries = function (x, y) {
|
||||
var bin = this.getBin(x, y);
|
||||
if (bin < 0 || bin >= this.fNcells) return 0;
|
||||
return this.fBinEntries[bin];
|
||||
}
|
||||
} else {
|
||||
m.getBin = function (x) {
|
||||
return x;
|
||||
};
|
||||
m.getBinContent = function (bin) {
|
||||
if (bin < 0 || bin >= this.fNcells) return 0;
|
||||
if (this.fBinEntries[bin] < 1e-300) return 0;
|
||||
if (!this.fArray) return 0;
|
||||
return this.fArray[bin] / this.fBinEntries[bin];
|
||||
};
|
||||
}
|
||||
m.getBinEffectiveEntries = function (bin) {
|
||||
if (bin < 0 || bin >= this.fNcells) return 0;
|
||||
var sumOfWeights = this.fBinEntries[bin];
|
||||
if (!this.fBinSumw2 || this.fBinSumw2.length !== this.fNcells) {
|
||||
// this can happen when reading an old file
|
||||
return sumOfWeights;
|
||||
}
|
||||
var sumOfWeightsSquare = this.fBinSumw2[bin];
|
||||
return (sumOfWeightsSquare > 0) ? sumOfWeights * sumOfWeights / sumOfWeightsSquare : 0;
|
||||
};
|
||||
m.getBinError = function (bin) {
|
||||
if (bin < 0 || bin >= this.fNcells) return 0;
|
||||
var cont = this.fArray[bin], // sum of bin w *y
|
||||
sum = this.fBinEntries[bin], // sum of bin weights
|
||||
err2 = this.fSumw2[bin], // sum of bin w * y^2
|
||||
neff = this.getBinEffectiveEntries(bin); // (sum of w)^2 / (sum of w^2)
|
||||
if (sum < 1e-300) return 0; // for empty bins
|
||||
var EErrorType = {kERRORMEAN: 0, kERRORSPREAD: 1, kERRORSPREADI: 2, kERRORSPREADG: 3};
|
||||
// case the values y are gaussian distributed y +/- sigma and w = 1/sigma^2
|
||||
if (this.fErrorMode === EErrorType.kERRORSPREADG)
|
||||
return 1.0 / Math.sqrt(sum);
|
||||
// compute variance in y (eprim2) and standard deviation in y (eprim)
|
||||
var contsum = cont / sum, eprim = Math.sqrt(Math.abs(err2 / sum - contsum * contsum));
|
||||
if (this.fErrorMode === EErrorType.kERRORSPREADI) {
|
||||
if (eprim !== 0) return eprim / Math.sqrt(neff);
|
||||
// in case content y is an integer (so each my has an error +/- 1/sqrt(12)
|
||||
// when the std(y) is zero
|
||||
return 1.0 / Math.sqrt(12 * neff);
|
||||
}
|
||||
// if approximate compute the sums (of w, wy and wy2) using all the bins
|
||||
// when the variance in y is zero
|
||||
// case option "S" return standard deviation in y
|
||||
if (this.fErrorMode === EErrorType.kERRORSPREAD) return eprim;
|
||||
// default case : fErrorMode = kERRORMEAN
|
||||
// return standard error on the mean of y
|
||||
return (eprim / Math.sqrt(neff));
|
||||
};
|
||||
}
|
||||
|
||||
if (typename === "TAxis") {
|
||||
m.GetBinLowEdge = function (bin) {
|
||||
if (this.fNbins <= 0) return 0;
|
||||
if ((this.fXbins.length > 0) && (bin > 0) && (bin <= this.fNbins)) return this.fXbins[bin - 1];
|
||||
return this.fXmin + (bin - 1) * (this.fXmax - this.fXmin) / this.fNbins;
|
||||
};
|
||||
m.GetBinCenter = function (bin) {
|
||||
if (this.fNbins <= 0) return 0;
|
||||
if ((this.fXbins.length > 0) && (bin > 0) && (bin < this.fNbins)) return (this.fXbins[bin - 1] + this.fXbins[bin]) / 2;
|
||||
return this.fXmin + (bin - 0.5) * (this.fXmax - this.fXmin) / this.fNbins;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof getMoreMethods == "function")
|
||||
getMoreMethods(m, typename, obj);
|
||||
|
||||
methodsCache[typename] = m;
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
/** @summary Adds specific methods to the object.
|
||||
*
|
||||
* JSROOT implements some basic methods for different ROOT classes.
|
||||
* @param {object} obj - object where methods are assigned
|
||||
* @param {string} typename - optional typename, if not specified, obj._typename will be used
|
||||
* @private
|
||||
*/
|
||||
function addMethods(obj, typename) {
|
||||
this.extend(obj, getMethods(typename || obj._typename, obj));
|
||||
}
|
||||
869
dataforge-vis-jsroot/src/main/resources/ThreeCSG.js
vendored
869
dataforge-vis-jsroot/src/main/resources/ThreeCSG.js
vendored
@@ -1,869 +0,0 @@
|
||||
import * as THREE from "three"
|
||||
|
||||
const EPSILON = 1e-5,
|
||||
COPLANAR = 0,
|
||||
FRONT = 1,
|
||||
BACK = 2,
|
||||
SPANNING = 3;
|
||||
|
||||
export function Geometry(geometry, transfer_matrix, nodeid, flippedMesh) {
|
||||
// Convert THREE.Geometry to ThreeBSP
|
||||
|
||||
if (geometry instanceof THREE.Geometry) {
|
||||
this.matrix = null; // new THREE.Matrix4; not create matrix when do not needed
|
||||
} else if (geometry instanceof THREE.Mesh) {
|
||||
// #todo: add hierarchy support
|
||||
geometry.updateMatrix();
|
||||
transfer_matrix = this.matrix = geometry.matrix.clone();
|
||||
geometry = geometry.geometry;
|
||||
} else if (geometry instanceof Node) {
|
||||
this.tree = geometry;
|
||||
this.matrix = null; // new THREE.Matrix4;
|
||||
return this;
|
||||
} else if (geometry instanceof THREE.BufferGeometry) {
|
||||
var pos_buf = geometry.getAttribute('position').array,
|
||||
norm_buf = geometry.getAttribute('normal').array,
|
||||
polygons = [], polygon, vert1, vert2, vert3;
|
||||
|
||||
for (var i = 0; i < pos_buf.length; i += 9) {
|
||||
polygon = new Polygon;
|
||||
|
||||
vert1 = new Vertex(pos_buf[i], pos_buf[i + 1], pos_buf[i + 2], norm_buf[i], norm_buf[i + 1], norm_buf[i + 2]);
|
||||
if (transfer_matrix) vert1.applyMatrix4(transfer_matrix);
|
||||
|
||||
vert2 = new Vertex(pos_buf[i + 3], pos_buf[i + 4], pos_buf[i + 5], norm_buf[i + 3], norm_buf[i + 4], norm_buf[i + 5]);
|
||||
if (transfer_matrix) vert2.applyMatrix4(transfer_matrix);
|
||||
|
||||
vert3 = new Vertex(pos_buf[i + 6], pos_buf[i + 7], pos_buf[i + 8], norm_buf[i + 6], norm_buf[i + 7], norm_buf[i + 8]);
|
||||
if (transfer_matrix) vert3.applyMatrix4(transfer_matrix);
|
||||
|
||||
if (flippedMesh) polygon.vertices.push(vert1, vert3, vert2);
|
||||
else polygon.vertices.push(vert1, vert2, vert3);
|
||||
|
||||
polygon.calculateProperties();
|
||||
polygons.push(polygon);
|
||||
}
|
||||
|
||||
this.tree = new Node(polygons, nodeid);
|
||||
if (nodeid !== undefined) this.maxid = this.tree.maxnodeid;
|
||||
return this;
|
||||
|
||||
} else if (geometry.polygons && (geometry.polygons[0] instanceof Polygon)) {
|
||||
var polygons = geometry.polygons;
|
||||
|
||||
for (var i = 0; i < polygons.length; ++i) {
|
||||
var polygon = polygons[i];
|
||||
if (transfer_matrix) {
|
||||
for (var n = 0; n < polygon.vertices.length; ++n)
|
||||
polygon.vertices[n].applyMatrix4(transfer_matrix);
|
||||
}
|
||||
|
||||
polygon.calculateProperties();
|
||||
}
|
||||
|
||||
this.tree = new Node(polygons, nodeid);
|
||||
if (nodeid !== undefined) this.maxid = this.tree.maxnodeid;
|
||||
return this;
|
||||
|
||||
} else {
|
||||
throw 'ThreeBSP: Given geometry is unsupported';
|
||||
}
|
||||
|
||||
var polygons = [],
|
||||
nfaces = geometry.faces.length,
|
||||
face, polygon, vertex;
|
||||
|
||||
for (var i = 0; i < nfaces; ++i) {
|
||||
face = geometry.faces[i];
|
||||
// faceVertexUvs = geometry.faceVertexUvs[0][i];
|
||||
polygon = new Polygon;
|
||||
|
||||
if (face instanceof THREE.Face3) {
|
||||
vertex = geometry.vertices[face.a];
|
||||
// uvs = faceVertexUvs ? new THREE.Vector2( faceVertexUvs[0].x, faceVertexUvs[0].y ) : null;
|
||||
vertex = new Vertex(vertex.x, vertex.y, vertex.z, face.vertexNormals[0].x, face.vertexNormals[0].y, face.vertexNormals[0].z /*face.normal , uvs */);
|
||||
if (transfer_matrix) vertex.applyMatrix4(transfer_matrix);
|
||||
polygon.vertices.push(vertex);
|
||||
|
||||
vertex = geometry.vertices[face.b];
|
||||
//uvs = faceVertexUvs ? new THREE.Vector2( faceVertexUvs[1].x, faceVertexUvs[1].y ) : null;
|
||||
vertex = new Vertex(vertex.x, vertex.y, vertex.z, face.vertexNormals[1].x, face.vertexNormals[1].y, face.vertexNormals[1].z/*face.normal , uvs */);
|
||||
if (transfer_matrix) vertex.applyMatrix4(transfer_matrix);
|
||||
polygon.vertices.push(vertex);
|
||||
|
||||
vertex = geometry.vertices[face.c];
|
||||
// uvs = faceVertexUvs ? new THREE.Vector2( faceVertexUvs[2].x, faceVertexUvs[2].y ) : null;
|
||||
vertex = new Vertex(vertex.x, vertex.y, vertex.z, face.vertexNormals[2].x, face.vertexNormals[2].y, face.vertexNormals[2].z /*face.normal, uvs */);
|
||||
if (transfer_matrix) vertex.applyMatrix4(transfer_matrix);
|
||||
polygon.vertices.push(vertex);
|
||||
} else {
|
||||
throw 'Invalid face type at index ' + i;
|
||||
}
|
||||
|
||||
polygon.calculateProperties();
|
||||
polygons.push(polygon);
|
||||
}
|
||||
|
||||
this.tree = new Node(polygons, nodeid);
|
||||
if (nodeid !== undefined) this.maxid = this.tree.maxnodeid;
|
||||
}
|
||||
|
||||
Geometry.prototype.subtract = function (other_tree) {
|
||||
var a = this.tree.clone(),
|
||||
b = other_tree.tree.clone();
|
||||
|
||||
a.invert();
|
||||
a.clipTo(b);
|
||||
b.clipTo(a);
|
||||
b.invert();
|
||||
b.clipTo(a);
|
||||
b.invert();
|
||||
a.build(b.allPolygons());
|
||||
a.invert();
|
||||
a = new Geometry(a);
|
||||
a.matrix = this.matrix;
|
||||
return a;
|
||||
};
|
||||
|
||||
Geometry.prototype.union = function (other_tree) {
|
||||
var a = this.tree.clone(),
|
||||
b = other_tree.tree.clone();
|
||||
|
||||
a.clipTo(b);
|
||||
b.clipTo(a);
|
||||
b.invert();
|
||||
b.clipTo(a);
|
||||
b.invert();
|
||||
a.build(b.allPolygons());
|
||||
a = new Geometry(a);
|
||||
a.matrix = this.matrix;
|
||||
return a;
|
||||
};
|
||||
|
||||
Geometry.prototype.intersect = function (other_tree) {
|
||||
var a = this.tree.clone(),
|
||||
b = other_tree.tree.clone();
|
||||
|
||||
a.invert();
|
||||
b.clipTo(a);
|
||||
b.invert();
|
||||
a.clipTo(b);
|
||||
b.clipTo(a);
|
||||
a.build(b.allPolygons());
|
||||
a.invert();
|
||||
a = new Geometry(a);
|
||||
a.matrix = this.matrix;
|
||||
return a;
|
||||
};
|
||||
|
||||
Geometry.prototype.tryToCompress = function (polygons) {
|
||||
|
||||
if (this.maxid === undefined) return;
|
||||
|
||||
var arr = [], parts, foundpair,
|
||||
nreduce = 0, n, len = polygons.length,
|
||||
p, p1, p2, i1, i2;
|
||||
|
||||
// sort out polygons
|
||||
for (n = 0; n < len; ++n) {
|
||||
p = polygons[n];
|
||||
if (p.id === undefined) continue;
|
||||
if (arr[p.id] === undefined) arr[p.id] = [];
|
||||
|
||||
arr[p.id].push(p);
|
||||
}
|
||||
|
||||
for (n = 0; n < arr.length; ++n) {
|
||||
parts = arr[n];
|
||||
if (parts === undefined) continue;
|
||||
|
||||
len = parts.length;
|
||||
|
||||
foundpair = (len > 1);
|
||||
|
||||
while (foundpair) {
|
||||
foundpair = false;
|
||||
|
||||
for (i1 = 0; i1 < len - 1; ++i1) {
|
||||
p1 = parts[i1];
|
||||
if (!p1 || !p1.parent) continue;
|
||||
for (i2 = i1 + 1; i2 < len; ++i2) {
|
||||
p2 = parts[i2];
|
||||
if (p2 && (p1.parent === p2.parent) && (p1.nsign === p2.nsign)) {
|
||||
|
||||
if (p1.nsign !== p1.parent.nsign) p1.parent.flip();
|
||||
|
||||
nreduce++;
|
||||
parts[i1] = p1.parent;
|
||||
parts[i2] = null;
|
||||
if (p1.parent.vertices.length < 3) console.log('something wrong with parent');
|
||||
foundpair = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nreduce > 0) {
|
||||
polygons.splice(0, polygons.length);
|
||||
|
||||
for (n = 0; n < arr.length; ++n) {
|
||||
parts = arr[n];
|
||||
if (parts !== undefined)
|
||||
for (i1 = 0, len = parts.length; i1 < len; ++i1)
|
||||
if (parts[i1]) polygons.push(parts[i1]);
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
Geometry.prototype.direct_subtract = function (other_tree) {
|
||||
var a = this.tree,
|
||||
b = other_tree.tree;
|
||||
a.invert();
|
||||
a.clipTo(b);
|
||||
b.clipTo(a);
|
||||
b.invert();
|
||||
b.clipTo(a);
|
||||
b.invert();
|
||||
a.build(b.collectPolygons([]));
|
||||
a.invert();
|
||||
return this;
|
||||
};
|
||||
|
||||
Geometry.prototype.direct_union = function (other_tree) {
|
||||
var a = this.tree,
|
||||
b = other_tree.tree;
|
||||
|
||||
a.clipTo(b);
|
||||
b.clipTo(a);
|
||||
b.invert();
|
||||
b.clipTo(a);
|
||||
b.invert();
|
||||
a.build(b.collectPolygons([]));
|
||||
return this;
|
||||
};
|
||||
|
||||
Geometry.prototype.direct_intersect = function (other_tree) {
|
||||
var a = this.tree,
|
||||
b = other_tree.tree;
|
||||
|
||||
a.invert();
|
||||
b.clipTo(a);
|
||||
b.invert();
|
||||
a.clipTo(b);
|
||||
b.clipTo(a);
|
||||
a.build(b.collectPolygons([]));
|
||||
a.invert();
|
||||
return this;
|
||||
};
|
||||
|
||||
export function CreateNormal(axis_name, pos, size) {
|
||||
// create geometry to make cut on specified axis
|
||||
|
||||
var vert1, vert2, vert3;
|
||||
|
||||
if (!size || (size < 10000)) size = 10000;
|
||||
|
||||
switch (axis_name) {
|
||||
case "x":
|
||||
vert1 = new Vertex(pos, -3 * size, size, 1, 0, 0),
|
||||
vert3 = new Vertex(pos, size, size, 1, 0, 0),
|
||||
vert2 = new Vertex(pos, size, -3 * size, 1, 0, 0);
|
||||
break;
|
||||
case "y":
|
||||
vert1 = new Vertex(-3 * size, pos, size, 0, 1, 0),
|
||||
vert2 = new Vertex(size, pos, size, 0, 1, 0),
|
||||
vert3 = new Vertex(size, pos, -3 * size, 0, 1, 0);
|
||||
break;
|
||||
case "z":
|
||||
vert1 = new Vertex(-3 * size, size, pos, 0, 0, 1),
|
||||
vert3 = new Vertex(size, size, pos, 0, 0, 1),
|
||||
vert2 = new Vertex(size, -3 * size, pos, 0, 0, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
var polygon = new Polygon([vert1, vert2, vert3]);
|
||||
polygon.calculateProperties();
|
||||
|
||||
var node = new Node([polygon]);
|
||||
|
||||
return new Geometry(node);
|
||||
}
|
||||
|
||||
Geometry.prototype.cut_from_plane = function (other_tree) {
|
||||
// just cut peaces from second geometry, which just simple plane
|
||||
|
||||
var a = this.tree,
|
||||
b = other_tree.tree;
|
||||
|
||||
a.invert();
|
||||
b.clipTo(a);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
Geometry.prototype.toGeometry = function () {
|
||||
var i, j,
|
||||
matrix = this.matrix ? new THREE.Matrix4().getInverse(this.matrix) : null,
|
||||
geometry = new THREE.Geometry(),
|
||||
polygons = this.tree.collectPolygons([]),
|
||||
polygon_count = polygons.length,
|
||||
polygon, polygon_vertice_count,
|
||||
vertice_dict = {},
|
||||
vertex_idx_a, vertex_idx_b, vertex_idx_c,
|
||||
vertex, face;
|
||||
|
||||
for (i = 0; i < polygon_count; ++i) {
|
||||
polygon = polygons[i];
|
||||
polygon_vertice_count = polygon.vertices.length;
|
||||
|
||||
for (j = 2; j < polygon_vertice_count; ++j) {
|
||||
// verticeUvs = [];
|
||||
|
||||
vertex = polygon.vertices[0];
|
||||
// verticeUvs.push( new THREE.Vector2( vertex.uv.x, vertex.uv.y ) );
|
||||
vertex = new THREE.Vector3(vertex.x, vertex.y, vertex.z);
|
||||
if (matrix) vertex.applyMatrix4(matrix);
|
||||
|
||||
if (typeof vertice_dict[vertex.x + ',' + vertex.y + ',' + vertex.z] !== 'undefined') {
|
||||
vertex_idx_a = vertice_dict[vertex.x + ',' + vertex.y + ',' + vertex.z];
|
||||
} else {
|
||||
geometry.vertices.push(vertex);
|
||||
vertex_idx_a = vertice_dict[vertex.x + ',' + vertex.y + ',' + vertex.z] = geometry.vertices.length - 1;
|
||||
}
|
||||
|
||||
vertex = polygon.vertices[j - 1];
|
||||
// verticeUvs.push( new THREE.Vector2( vertex.uv.x, vertex.uv.y ) );
|
||||
vertex = new THREE.Vector3(vertex.x, vertex.y, vertex.z);
|
||||
if (matrix) vertex.applyMatrix4(matrix);
|
||||
if (typeof vertice_dict[vertex.x + ',' + vertex.y + ',' + vertex.z] !== 'undefined') {
|
||||
vertex_idx_b = vertice_dict[vertex.x + ',' + vertex.y + ',' + vertex.z];
|
||||
} else {
|
||||
geometry.vertices.push(vertex);
|
||||
vertex_idx_b = vertice_dict[vertex.x + ',' + vertex.y + ',' + vertex.z] = geometry.vertices.length - 1;
|
||||
}
|
||||
|
||||
vertex = polygon.vertices[j];
|
||||
// verticeUvs.push( new THREE.Vector2( vertex.uv.x, vertex.uv.y ) );
|
||||
vertex = new THREE.Vector3(vertex.x, vertex.y, vertex.z);
|
||||
if (matrix) vertex.applyMatrix4(matrix);
|
||||
if (typeof vertice_dict[vertex.x + ',' + vertex.y + ',' + vertex.z] !== 'undefined') {
|
||||
vertex_idx_c = vertice_dict[vertex.x + ',' + vertex.y + ',' + vertex.z];
|
||||
} else {
|
||||
geometry.vertices.push(vertex);
|
||||
vertex_idx_c = vertice_dict[vertex.x + ',' + vertex.y + ',' + vertex.z] = geometry.vertices.length - 1;
|
||||
}
|
||||
|
||||
face = new THREE.Face3(
|
||||
vertex_idx_a,
|
||||
vertex_idx_b,
|
||||
vertex_idx_c,
|
||||
new THREE.Vector3(polygon.normal.x, polygon.normal.y, polygon.normal.z)
|
||||
);
|
||||
|
||||
geometry.faces.push(face);
|
||||
// geometry.faceVertexUvs[0].push( verticeUvs );
|
||||
}
|
||||
|
||||
}
|
||||
return geometry;
|
||||
};
|
||||
|
||||
Geometry.prototype.scale = function (x, y, z) {
|
||||
// try to scale as THREE.BufferGeometry
|
||||
var polygons = this.tree.collectPolygons([]);
|
||||
|
||||
for (var i = 0; i < polygons.length; ++i) {
|
||||
var polygon = polygons[i];
|
||||
for (var k = 0; k < polygon.vertices.length; ++k) {
|
||||
var v = polygon.vertices[k];
|
||||
v.x *= x;
|
||||
v.y *= y;
|
||||
v.z *= z;
|
||||
}
|
||||
delete polygon.normal;
|
||||
polygon.calculateProperties();
|
||||
}
|
||||
};
|
||||
|
||||
Geometry.prototype.toPolygons = function () {
|
||||
var polygons = this.tree.collectPolygons([]);
|
||||
|
||||
this.tryToCompress(polygons);
|
||||
|
||||
for (var i = 0; i < polygons.length; ++i) {
|
||||
delete polygons[i].id;
|
||||
delete polygons[i].parent;
|
||||
}
|
||||
|
||||
return polygons;
|
||||
};
|
||||
|
||||
Geometry.prototype.toBufferGeometry = function () {
|
||||
return CreateBufferGeometry(this.toPolygons());
|
||||
};
|
||||
|
||||
export function CreateBufferGeometry(polygons) {
|
||||
var i, j, polygon_count = polygons.length, buf_size = 0;
|
||||
|
||||
for (i = 0; i < polygon_count; ++i)
|
||||
buf_size += (polygons[i].vertices.length - 2) * 9;
|
||||
|
||||
var positions_buf = new Float32Array(buf_size),
|
||||
normals_buf = new Float32Array(buf_size),
|
||||
iii = 0, polygon;
|
||||
|
||||
function CopyVertex(vertex) {
|
||||
|
||||
positions_buf[iii] = vertex.x;
|
||||
positions_buf[iii + 1] = vertex.y;
|
||||
positions_buf[iii + 2] = vertex.z;
|
||||
|
||||
normals_buf[iii] = polygon.nsign * vertex.nx;
|
||||
normals_buf[iii + 1] = polygon.nsign * vertex.ny;
|
||||
normals_buf[iii + 2] = polygon.nsign * vertex.nz;
|
||||
iii += 3;
|
||||
}
|
||||
|
||||
for (i = 0; i < polygon_count; ++i) {
|
||||
polygon = polygons[i];
|
||||
for (j = 2; j < polygon.vertices.length; ++j) {
|
||||
CopyVertex(polygon.vertices[0]);
|
||||
CopyVertex(polygon.vertices[j - 1]);
|
||||
CopyVertex(polygon.vertices[j]);
|
||||
}
|
||||
}
|
||||
|
||||
var geometry = new THREE.BufferGeometry();
|
||||
geometry.addAttribute('position', new THREE.BufferAttribute(positions_buf, 3));
|
||||
geometry.addAttribute('normal', new THREE.BufferAttribute(normals_buf, 3));
|
||||
|
||||
// geometry.computeVertexNormals();
|
||||
return geometry;
|
||||
}
|
||||
|
||||
Geometry.prototype.toMesh = function (material) {
|
||||
var geometry = this.toGeometry(),
|
||||
mesh = new THREE.Mesh(geometry, material);
|
||||
|
||||
if (this.matrix) {
|
||||
mesh.position.setFromMatrixPosition(this.matrix);
|
||||
mesh.rotation.setFromRotationMatrix(this.matrix);
|
||||
}
|
||||
|
||||
return mesh;
|
||||
};
|
||||
|
||||
export class Polygon {
|
||||
constructor(vertices, normal, w) {
|
||||
if (!(vertices instanceof Array)) {
|
||||
vertices = [];
|
||||
}
|
||||
|
||||
this.vertices = vertices;
|
||||
this.nsign = 1;
|
||||
if (vertices.length > 0) {
|
||||
this.calculateProperties();
|
||||
} else {
|
||||
this.normal = this.w = undefined;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Polygon.prototype.copyProperties = function (parent, more) {
|
||||
this.normal = parent.normal; // .clone();
|
||||
this.w = parent.w;
|
||||
this.nsign = parent.nsign;
|
||||
if (more && (parent.id !== undefined)) {
|
||||
this.id = parent.id;
|
||||
this.parent = parent;
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
Polygon.prototype.calculateProperties = function () {
|
||||
if (this.normal) return;
|
||||
|
||||
var a = this.vertices[0],
|
||||
b = this.vertices[1],
|
||||
c = this.vertices[2];
|
||||
|
||||
this.nsign = 1;
|
||||
|
||||
this.normal = b.clone().subtract(a).cross(
|
||||
c.clone().subtract(a)
|
||||
).normalize();
|
||||
|
||||
this.w = this.normal.clone().dot(a);
|
||||
return this;
|
||||
};
|
||||
|
||||
Polygon.prototype.clone = function () {
|
||||
var vertice_count = this.vertices.length,
|
||||
polygon = new Polygon;
|
||||
|
||||
for (var i = 0; i < vertice_count; ++i)
|
||||
polygon.vertices.push(this.vertices[i].clone());
|
||||
|
||||
return polygon.copyProperties(this);
|
||||
};
|
||||
|
||||
Polygon.prototype.flip = function () {
|
||||
|
||||
/// normal is not changed, only sign variable
|
||||
//this.normal.multiplyScalar( -1 );
|
||||
//this.w *= -1;
|
||||
|
||||
this.nsign *= -1;
|
||||
|
||||
this.vertices.reverse();
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Polygon.prototype.classifyVertex = function (vertex) {
|
||||
var side_value = this.nsign * (this.normal.dot(vertex) - this.w);
|
||||
|
||||
if (side_value < -EPSILON) return BACK;
|
||||
if (side_value > EPSILON) return FRONT;
|
||||
return COPLANAR;
|
||||
};
|
||||
|
||||
Polygon.prototype.classifySide = function (polygon) {
|
||||
var i, classification,
|
||||
num_positive = 0, num_negative = 0,
|
||||
vertice_count = polygon.vertices.length;
|
||||
|
||||
for (i = 0; i < vertice_count; ++i) {
|
||||
classification = this.classifyVertex(polygon.vertices[i]);
|
||||
if (classification === FRONT) {
|
||||
++num_positive;
|
||||
} else if (classification === BACK) {
|
||||
++num_negative;
|
||||
}
|
||||
}
|
||||
|
||||
if (num_positive > 0 && num_negative === 0) return FRONT;
|
||||
if (num_positive === 0 && num_negative > 0) return BACK;
|
||||
if (num_positive === 0 && num_negative === 0) return COPLANAR;
|
||||
return SPANNING;
|
||||
};
|
||||
|
||||
Polygon.prototype.splitPolygon = function (polygon, coplanar_front, coplanar_back, front, back) {
|
||||
var classification = this.classifySide(polygon);
|
||||
|
||||
if (classification === COPLANAR) {
|
||||
|
||||
((this.nsign * polygon.nsign * this.normal.dot(polygon.normal) > 0) ? coplanar_front : coplanar_back).push(polygon);
|
||||
|
||||
} else if (classification === FRONT) {
|
||||
|
||||
front.push(polygon);
|
||||
|
||||
} else if (classification === BACK) {
|
||||
|
||||
back.push(polygon);
|
||||
|
||||
} else {
|
||||
|
||||
var vertice_count = polygon.vertices.length,
|
||||
nnx = this.normal.x,
|
||||
nny = this.normal.y,
|
||||
nnz = this.normal.z,
|
||||
i, j, ti, tj, vi, vj,
|
||||
t, v,
|
||||
f = [], b = [];
|
||||
|
||||
for (i = 0; i < vertice_count; ++i) {
|
||||
|
||||
j = (i + 1) % vertice_count;
|
||||
vi = polygon.vertices[i];
|
||||
vj = polygon.vertices[j];
|
||||
ti = this.classifyVertex(vi);
|
||||
tj = this.classifyVertex(vj);
|
||||
|
||||
if (ti != BACK) f.push(vi);
|
||||
if (ti != FRONT) b.push(vi);
|
||||
if ((ti | tj) === SPANNING) {
|
||||
// t = ( this.w - this.normal.dot( vi ) ) / this.normal.dot( vj.clone().subtract( vi ) );
|
||||
//v = vi.clone().lerp( vj, t );
|
||||
|
||||
t = (this.w - (nnx * vi.x + nny * vi.y + nnz * vi.z)) / (nnx * (vj.x - vi.x) + nny * (vj.y - vi.y) + nnz * (vj.z - vi.z));
|
||||
|
||||
v = vi.interpolate(vj, t);
|
||||
f.push(v);
|
||||
b.push(v);
|
||||
}
|
||||
}
|
||||
|
||||
//if ( f.length >= 3 ) front.push( new Polygon( f ).calculateProperties() );
|
||||
//if ( b.length >= 3 ) back.push( new Polygon( b ).calculateProperties() );
|
||||
if (f.length >= 3) front.push(new Polygon(f).copyProperties(polygon, true));
|
||||
if (b.length >= 3) back.push(new Polygon(b).copyProperties(polygon, true));
|
||||
}
|
||||
};
|
||||
|
||||
export class Vertex {
|
||||
constructor(x, y, z, nx, ny, nz) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.nx = nx;
|
||||
this.ny = ny;
|
||||
this.nz = nz;
|
||||
};
|
||||
}
|
||||
|
||||
Vertex.prototype.setnormal = function (nx, ny, nz) {
|
||||
this.nx = nx;
|
||||
this.ny = ny;
|
||||
this.nz = nz;
|
||||
};
|
||||
|
||||
Vertex.prototype.clone = function () {
|
||||
return new Vertex(this.x, this.y, this.z, this.nx, this.ny, this.nz);
|
||||
};
|
||||
|
||||
Vertex.prototype.add = function (vertex) {
|
||||
this.x += vertex.x;
|
||||
this.y += vertex.y;
|
||||
this.z += vertex.z;
|
||||
return this;
|
||||
};
|
||||
|
||||
Vertex.prototype.subtract = function (vertex) {
|
||||
this.x -= vertex.x;
|
||||
this.y -= vertex.y;
|
||||
this.z -= vertex.z;
|
||||
return this;
|
||||
};
|
||||
|
||||
Vertex.prototype.multiplyScalar = function (scalar) {
|
||||
this.x *= scalar;
|
||||
this.y *= scalar;
|
||||
this.z *= scalar;
|
||||
return this;
|
||||
};
|
||||
|
||||
Vertex.prototype.cross = function (vertex) {
|
||||
var x = this.x,
|
||||
y = this.y,
|
||||
z = this.z;
|
||||
|
||||
this.x = y * vertex.z - z * vertex.y;
|
||||
this.y = z * vertex.x - x * vertex.z;
|
||||
this.z = x * vertex.y - y * vertex.x;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Vertex.prototype.normalize = function () {
|
||||
var length = Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
|
||||
|
||||
this.x /= length;
|
||||
this.y /= length;
|
||||
this.z /= length;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Vertex.prototype.dot = function (vertex) {
|
||||
return this.x * vertex.x + this.y * vertex.y + this.z * vertex.z;
|
||||
};
|
||||
|
||||
Vertex.prototype.diff = function (vertex) {
|
||||
var dx = (this.x - vertex.x),
|
||||
dy = (this.y - vertex.y),
|
||||
dz = (this.z - vertex.z),
|
||||
len2 = this.x * this.x + this.y * this.y + this.z * this.z;
|
||||
|
||||
return (dx * dx + dy * dy + dz * dz) / (len2 > 0 ? len2 : 1e-10);
|
||||
};
|
||||
|
||||
/*
|
||||
Vertex.prototype.lerp = function( a, t ) {
|
||||
this.add(
|
||||
a.clone().subtract( this ).multiplyScalar( t )
|
||||
);
|
||||
|
||||
this.normal.add(
|
||||
a.normal.clone().sub( this.normal ).multiplyScalar( t )
|
||||
);
|
||||
|
||||
//this.uv.add(
|
||||
// a.uv.clone().sub( this.uv ).multiplyScalar( t )
|
||||
//);
|
||||
|
||||
return this;
|
||||
};
|
||||
Vertex.prototype.interpolate = function( other, t ) {
|
||||
return this.clone().lerp( other, t );
|
||||
};
|
||||
*/
|
||||
|
||||
Vertex.prototype.interpolate = function (a, t) {
|
||||
var t1 = 1 - t;
|
||||
return new Vertex(this.x * t1 + a.x * t, this.y * t1 + a.y * t, this.z * t1 + a.z * t,
|
||||
this.nx * t1 + a.nx * t, this.ny * t1 + a.ny * t, this.nz * t1 + a.nz * t);
|
||||
};
|
||||
|
||||
Vertex.prototype.applyMatrix4 = function (m) {
|
||||
|
||||
// input: THREE.Matrix4 affine matrix
|
||||
|
||||
var x = this.x, y = this.y, z = this.z, e = m.elements;
|
||||
|
||||
this.x = e[0] * x + e[4] * y + e[8] * z + e[12];
|
||||
this.y = e[1] * x + e[5] * y + e[9] * z + e[13];
|
||||
this.z = e[2] * x + e[6] * y + e[10] * z + e[14];
|
||||
|
||||
x = this.nx;
|
||||
y = this.ny;
|
||||
z = this.nz;
|
||||
|
||||
this.nx = e[0] * x + e[4] * y + e[8] * z;
|
||||
this.ny = e[1] * x + e[5] * y + e[9] * z;
|
||||
this.nz = e[2] * x + e[6] * y + e[10] * z;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
// ================================================================================================
|
||||
|
||||
export class Node {
|
||||
constructor(polygons, nodeid) {
|
||||
this.polygons = [];
|
||||
this.front = this.back = undefined;
|
||||
|
||||
if (!(polygons instanceof Array) || polygons.length === 0) return;
|
||||
|
||||
this.divider = polygons[0].clone();
|
||||
|
||||
var polygon_count = polygons.length,
|
||||
front = [], back = [];
|
||||
|
||||
for (var i = 0; i < polygon_count; ++i) {
|
||||
if (nodeid !== undefined) {
|
||||
polygons[i].id = nodeid++;
|
||||
delete polygons[i].parent;
|
||||
}
|
||||
|
||||
this.divider.splitPolygon(polygons[i], this.polygons, this.polygons, front, back);
|
||||
}
|
||||
|
||||
if (nodeid !== undefined) this.maxnodeid = nodeid;
|
||||
|
||||
if (front.length > 0)
|
||||
this.front = new Node(front);
|
||||
|
||||
if (back.length > 0)
|
||||
this.back = new Node(back);
|
||||
};
|
||||
}
|
||||
|
||||
Node.isConvex = function (polygons) {
|
||||
var i, j, len = polygons.length;
|
||||
for (i = 0; i < len; ++i)
|
||||
for (j = 0; j < len; ++j)
|
||||
if (i !== j && polygons[i].classifySide(polygons[j]) !== BACK) return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
Node.prototype.build = function (polygons) {
|
||||
var polygon_count = polygons.length,
|
||||
front = [], back = [];
|
||||
|
||||
if (!this.divider)
|
||||
this.divider = polygons[0].clone();
|
||||
|
||||
for (var i = 0; i < polygon_count; ++i)
|
||||
this.divider.splitPolygon(polygons[i], this.polygons, this.polygons, front, back);
|
||||
|
||||
if (front.length > 0) {
|
||||
if (!this.front) this.front = new Node();
|
||||
this.front.build(front);
|
||||
}
|
||||
|
||||
if (back.length > 0) {
|
||||
if (!this.back) this.back = new Node();
|
||||
this.back.build(back);
|
||||
}
|
||||
};
|
||||
|
||||
Node.prototype.collectPolygons = function (arr) {
|
||||
var len = this.polygons.length;
|
||||
for (var i = 0; i < len; ++i) arr.push(this.polygons[i]);
|
||||
if (this.front) this.front.collectPolygons(arr);
|
||||
if (this.back) this.back.collectPolygons(arr);
|
||||
return arr;
|
||||
};
|
||||
|
||||
Node.prototype.allPolygons = function () {
|
||||
var polygons = this.polygons.slice();
|
||||
if (this.front) polygons = polygons.concat(this.front.allPolygons());
|
||||
if (this.back) polygons = polygons.concat(this.back.allPolygons());
|
||||
return polygons;
|
||||
};
|
||||
|
||||
Node.prototype.numPolygons = function () {
|
||||
var res = this.polygons.length;
|
||||
if (this.front) res += this.front.numPolygons();
|
||||
if (this.back) res += this.back.numPolygons();
|
||||
return res;
|
||||
};
|
||||
|
||||
Node.prototype.clone = function () {
|
||||
var node = new Node();
|
||||
|
||||
node.divider = this.divider.clone();
|
||||
node.polygons = this.polygons.map(function (polygon) {
|
||||
return polygon.clone();
|
||||
});
|
||||
node.front = this.front && this.front.clone();
|
||||
node.back = this.back && this.back.clone();
|
||||
|
||||
return node;
|
||||
};
|
||||
|
||||
Node.prototype.invert = function () {
|
||||
var polygon_count = this.polygons.length;
|
||||
|
||||
for (var i = 0; i < polygon_count; ++i)
|
||||
this.polygons[i].flip();
|
||||
|
||||
this.divider.flip();
|
||||
if (this.front) this.front.invert();
|
||||
if (this.back) this.back.invert();
|
||||
|
||||
var temp = this.front;
|
||||
this.front = this.back;
|
||||
this.back = temp;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Node.prototype.clipPolygons = function (polygons) {
|
||||
|
||||
if (!this.divider) return polygons.slice();
|
||||
|
||||
var polygon_count = polygons.length, front = [], back = [];
|
||||
|
||||
for (var i = 0; i < polygon_count; ++i)
|
||||
this.divider.splitPolygon(polygons[i], front, back, front, back);
|
||||
|
||||
if (this.front) front = this.front.clipPolygons(front);
|
||||
if (this.back) back = this.back.clipPolygons(back);
|
||||
else back = [];
|
||||
|
||||
return front.concat(back);
|
||||
};
|
||||
|
||||
Node.prototype.clipTo = function (node) {
|
||||
this.polygons = node.clipPolygons(this.polygons);
|
||||
if (this.front) this.front.clipTo(node);
|
||||
if (this.back) this.back.clipTo(node);
|
||||
};
|
||||
@@ -1,33 +0,0 @@
|
||||
<!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="visionforge-spatial-gdml-0.1.0-dev.js"></script>
|
||||
</head>
|
||||
<body class="application">
|
||||
<div class="container" id="drop_zone" data-toggle="tooltip" data-placement="right"
|
||||
title="Для загрузки данных в текстовом формате, надо перетащить файл сюда">
|
||||
Загрузить данные
|
||||
<br/>
|
||||
(перетащить файл сюда)
|
||||
</div>
|
||||
<div class="container">
|
||||
<h1>Demo grid</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>
|
||||
@@ -12,4 +12,4 @@ drag-and-drop GDML file to the window to see visualization. For an example file,
|
||||
|
||||
##### Example view:
|
||||
|
||||

|
||||

|
||||
|
||||
@@ -1,25 +1,29 @@
|
||||
import scientifik.DependencyConfiguration
|
||||
import scientifik.FXModule
|
||||
import scientifik.useFx
|
||||
import ru.mipt.npm.gradle.DependencyConfiguration
|
||||
import ru.mipt.npm.gradle.FXModule
|
||||
|
||||
plugins {
|
||||
id("scientifik.mpp")
|
||||
id("application")
|
||||
id("ru.mipt.npm.gradle.mpp")
|
||||
application
|
||||
}
|
||||
|
||||
val fxVersion: String by rootProject.extra
|
||||
useFx(FXModule.CONTROLS, version = fxVersion, configuration = DependencyConfiguration.IMPLEMENTATION)
|
||||
kscience {
|
||||
val fxVersion: String by rootProject.extra
|
||||
useFx(FXModule.CONTROLS, version = fxVersion, configuration = DependencyConfiguration.IMPLEMENTATION)
|
||||
application()
|
||||
}
|
||||
|
||||
kotlin {
|
||||
|
||||
jvm {
|
||||
withJava()
|
||||
}
|
||||
|
||||
js {
|
||||
js{
|
||||
useCommonJs()
|
||||
browser {
|
||||
commonWebpackConfig {
|
||||
cssSupport.enabled = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
commonMain {
|
||||
dependencies {
|
||||
@@ -27,9 +31,15 @@ kotlin {
|
||||
implementation(project(":visionforge-gdml"))
|
||||
}
|
||||
}
|
||||
jsMain{
|
||||
jvmMain {
|
||||
dependencies {
|
||||
implementation(project(":ui:bootstrap"))
|
||||
implementation(project(":visionforge-fx"))
|
||||
}
|
||||
}
|
||||
jsMain {
|
||||
dependencies {
|
||||
implementation(project(":ui:ring"))
|
||||
implementation(project(":visionforge-threejs"))
|
||||
implementation(npm("react-file-drop", "3.0.6"))
|
||||
}
|
||||
}
|
||||
@@ -37,11 +47,11 @@ kotlin {
|
||||
}
|
||||
|
||||
application {
|
||||
mainClassName = "hep.dataforge.vision.gdml.demo.GDMLDemoAppKt"
|
||||
mainClass.set("space.kscience.visionforge.gdml.demo.GdmlFxDemoAppKt")
|
||||
}
|
||||
|
||||
val convertGdmlToJson by tasks.creating(JavaExec::class) {
|
||||
group = "application"
|
||||
classpath = sourceSets["main"].runtimeClasspath
|
||||
main = "hep.dataforge.vis.spatial.gdml.demo.SaveToJsonKt"
|
||||
main = "space.kscience.dataforge.vis.spatial.gdml.demo.SaveToJsonKt"
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
package hep.dataforge.vision.gdml.demo
|
||||
|
||||
import scientifik.gdml.*
|
||||
|
||||
|
||||
fun cubes(): GDML = GDML {
|
||||
val center = define.position("center")
|
||||
structure {
|
||||
val air = ref<GDMLMaterial>("G4_AIR")
|
||||
val tubeMaterial = ref<GDMLMaterial>("tube")
|
||||
val boxMaterial = ref<GDMLMaterial>("box")
|
||||
val segment = solids.tube("segment", 20, 5.0) {
|
||||
rmin = 17
|
||||
deltaphi = 60
|
||||
aunit = AUnit.DEG.title
|
||||
}
|
||||
val worldBox = solids.box("LargeBox", 200, 200, 200)
|
||||
val smallBox = solids.box("smallBox", 30, 30, 30)
|
||||
val segmentVolume = volume("segment", tubeMaterial, segment.ref()) {}
|
||||
val circle = volume("composite", boxMaterial, smallBox.ref()) {
|
||||
for (i in 0 until 6) {
|
||||
physVolume(segmentVolume) {
|
||||
name = "segment$i"
|
||||
positionref = center.ref()
|
||||
rotation {
|
||||
z = 60 * i
|
||||
unit = AUnit.DEG.title
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
world = volume("world", air, worldBox.ref()) {
|
||||
for (i in 0 until 3) {
|
||||
for (j in 0 until 3) {
|
||||
for (k in 0 until 3) {
|
||||
physVolume(circle) {
|
||||
name = "composite$i$j$k"
|
||||
position {
|
||||
x = (-50 + i * 50)
|
||||
y = (-50 + j * 50)
|
||||
z = (-50 + k * 50)
|
||||
}
|
||||
rotation {
|
||||
x = i * 120
|
||||
y = j * 120
|
||||
z = 120 * k
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package hep.dataforge.vision.gdml
|
||||
|
||||
import hep.dataforge.meta.setItem
|
||||
import hep.dataforge.meta.string
|
||||
import hep.dataforge.names.toName
|
||||
import hep.dataforge.values.asValue
|
||||
import hep.dataforge.vision.gdml.demo.cubes
|
||||
import hep.dataforge.vision.solid.SolidMaterial
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class GDMLVisualTest {
|
||||
@Test
|
||||
fun testPrototypeProperty() {
|
||||
val gdml = cubes()
|
||||
val visual = gdml.toVision()
|
||||
visual["composite000.segment0".toName()]?.setItem(SolidMaterial.MATERIAL_COLOR_KEY, "red".asValue())
|
||||
assertEquals("red", visual["composite000.segment0".toName()]?.getItem(SolidMaterial.MATERIAL_COLOR_KEY).string)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package space.kscience.visionforge.gdml
|
||||
|
||||
import space.kscience.dataforge.meta.string
|
||||
import space.kscience.dataforge.names.toName
|
||||
import space.kscience.dataforge.values.asValue
|
||||
import space.kscience.gdml.GdmlShowCase
|
||||
import space.kscience.visionforge.setProperty
|
||||
import space.kscience.visionforge.solid.SolidMaterial
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotNull
|
||||
|
||||
class GDMLVisionTest {
|
||||
|
||||
// @Test
|
||||
// fun testCubesStyles(){
|
||||
// val cubes = gdml.toVision()
|
||||
// val segment = cubes["composite000.segment_0".toName()] as Solid
|
||||
// println(segment.styles)
|
||||
// println(segment.material)
|
||||
// }
|
||||
|
||||
|
||||
@Test
|
||||
fun testPrototypeProperty() {
|
||||
val vision = GdmlShowCase.cubes().toVision()
|
||||
val child = vision["composite-000.segment-0".toName()]
|
||||
assertNotNull(child)
|
||||
child.setProperty(SolidMaterial.MATERIAL_COLOR_KEY, "red".asValue())
|
||||
assertEquals("red", child.getProperty(SolidMaterial.MATERIAL_COLOR_KEY).string)
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,9 @@ package drop
|
||||
|
||||
import org.w3c.dom.DragEvent
|
||||
import org.w3c.files.FileList
|
||||
import react.*
|
||||
import react.Component
|
||||
import react.RProps
|
||||
import react.RState
|
||||
|
||||
external enum class DropEffects {
|
||||
copy,
|
||||
|
||||
@@ -1,154 +0,0 @@
|
||||
package hep.dataforge.vision.gdml.demo
|
||||
|
||||
import hep.dataforge.context.Context
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.isEmpty
|
||||
import hep.dataforge.vision.Vision
|
||||
import hep.dataforge.vision.VisionGroup
|
||||
import hep.dataforge.vision.bootstrap.*
|
||||
import hep.dataforge.vision.gdml.toVision
|
||||
import hep.dataforge.vision.react.component
|
||||
import hep.dataforge.vision.react.configEditor
|
||||
import hep.dataforge.vision.react.flexColumn
|
||||
import hep.dataforge.vision.react.state
|
||||
import hep.dataforge.vision.solid.Solid
|
||||
import hep.dataforge.vision.solid.SolidGroup
|
||||
import hep.dataforge.vision.solid.specifications.Camera
|
||||
import hep.dataforge.vision.solid.specifications.Canvas3DOptions
|
||||
import hep.dataforge.vision.solid.three.ThreeCanvas
|
||||
import hep.dataforge.vision.solid.three.ThreeCanvasComponent
|
||||
import hep.dataforge.vision.solid.three.canvasControls
|
||||
import kotlinx.css.FlexBasis
|
||||
import kotlinx.css.Overflow
|
||||
import kotlinx.css.flex
|
||||
import kotlinx.css.overflow
|
||||
import org.w3c.files.FileReader
|
||||
import org.w3c.files.get
|
||||
import react.RProps
|
||||
import react.dom.h1
|
||||
import scientifik.gdml.GDML
|
||||
import scientifik.gdml.parse
|
||||
import styled.css
|
||||
import styled.styledDiv
|
||||
import kotlin.browser.window
|
||||
import kotlin.math.PI
|
||||
|
||||
interface GDMLAppProps : RProps {
|
||||
var context: Context
|
||||
var rootObject: Vision?
|
||||
var selected: Name?
|
||||
}
|
||||
|
||||
private val canvasConfig = Canvas3DOptions {
|
||||
camera = Camera {
|
||||
distance = 2100.0
|
||||
latitude = PI / 6
|
||||
azimuth = PI + PI / 6
|
||||
}
|
||||
}
|
||||
|
||||
val GDMLApp = component<GDMLAppProps> { props ->
|
||||
var selected by state { props.selected }
|
||||
var canvas: ThreeCanvas? by state { null }
|
||||
var visual: Vision? by state { props.rootObject }
|
||||
|
||||
val select: (Name?) -> Unit = {
|
||||
selected = it
|
||||
}
|
||||
|
||||
fun loadData(name: String, data: String) {
|
||||
visual = when {
|
||||
name.endsWith(".gdml") || name.endsWith(".xml") -> {
|
||||
val gdml = GDML.parse(data)
|
||||
gdml.toVision(gdmlConfiguration)
|
||||
}
|
||||
name.endsWith(".json") -> SolidGroup.parseJson(data)
|
||||
else -> {
|
||||
window.alert("File extension is not recognized: $name")
|
||||
error("File extension is not recognized: $name")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flexColumn {
|
||||
css {
|
||||
flex(1.0, 1.0, FlexBasis.auto)
|
||||
}
|
||||
h1 { +"GDML/JSON loader demo" }
|
||||
styledDiv {
|
||||
css {
|
||||
classes.add("row")
|
||||
classes.add("p-1")
|
||||
overflow = Overflow.auto
|
||||
}
|
||||
gridColumn(3, maxSize= GridMaxSize.XL, classes = "order-2 order-xl-1") {
|
||||
card("Load data") {
|
||||
fileDrop("(drag file here)") { files ->
|
||||
val file = files?.get(0)
|
||||
if (file != null) {
|
||||
|
||||
FileReader().apply {
|
||||
onload = {
|
||||
val string = result as String
|
||||
loadData(file.name, string)
|
||||
}
|
||||
readAsText(file)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//tree
|
||||
card("Object tree", "overflow-auto") {
|
||||
visual?.let {
|
||||
objectTree(it, selected, select)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gridColumn(6, maxSize= GridMaxSize.XL, classes = "order-1 order-xl-2") {
|
||||
//canvas
|
||||
(visual as? Solid)?.let { visual3D ->
|
||||
child(ThreeCanvasComponent::class) {
|
||||
attrs {
|
||||
this.context = props.context
|
||||
this.obj = visual3D
|
||||
this.selected = selected
|
||||
this.clickCallback = select
|
||||
this.canvasCallback = {
|
||||
canvas = it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
gridColumn(3, maxSize= GridMaxSize.XL, classes = "order-3") {
|
||||
container {
|
||||
//settings
|
||||
canvas?.let {
|
||||
card("Canvas configuration") {
|
||||
canvasControls(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
container {
|
||||
namecrumbs(selected, "World") { selected = it }
|
||||
}
|
||||
container {
|
||||
//properties
|
||||
card("Properties") {
|
||||
selected.let { selected ->
|
||||
val selectedObject: Vision? = when {
|
||||
selected == null -> null
|
||||
selected.isEmpty() -> visual
|
||||
else -> (visual as? VisionGroup)?.get(selected)
|
||||
}
|
||||
if (selectedObject != null) {
|
||||
configEditor(selectedObject, default = selectedObject.getAllProperties(), key = selected)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
package hep.dataforge.vision.gdml.demo
|
||||
|
||||
import hep.dataforge.context.Global
|
||||
import hep.dataforge.js.Application
|
||||
import hep.dataforge.js.startApplication
|
||||
import hep.dataforge.vision.gdml.GDMLTransformer
|
||||
import hep.dataforge.vision.gdml.LUnit
|
||||
import hep.dataforge.vision.gdml.toVision
|
||||
import hep.dataforge.vision.solid.SolidMaterial.Companion.MATERIAL_OPACITY_KEY
|
||||
import kotlinx.css.*
|
||||
import react.child
|
||||
import react.dom.render
|
||||
import styled.injectGlobal
|
||||
import kotlin.browser.document
|
||||
|
||||
|
||||
val gdmlConfiguration: GDMLTransformer.() -> Unit = {
|
||||
lUnit = LUnit.CM
|
||||
volumeAction = { volume ->
|
||||
when {
|
||||
volume.name.startsWith("ecal01lay") -> GDMLTransformer.Action.REJECT
|
||||
volume.name.startsWith("UPBL") -> GDMLTransformer.Action.REJECT
|
||||
volume.name.startsWith("USCL") -> GDMLTransformer.Action.REJECT
|
||||
volume.name.startsWith("VPBL") -> GDMLTransformer.Action.REJECT
|
||||
volume.name.startsWith("VSCL") -> GDMLTransformer.Action.REJECT
|
||||
else -> GDMLTransformer.Action.CACHE
|
||||
}
|
||||
}
|
||||
|
||||
solidConfiguration = { parent, solid ->
|
||||
if (
|
||||
solid.name.startsWith("Yoke")
|
||||
|| solid.name.startsWith("Pole")
|
||||
|| parent.physVolumes.isNotEmpty()
|
||||
) {
|
||||
useStyle("opaque") {
|
||||
MATERIAL_OPACITY_KEY put 0.3
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class GDMLDemoApp : Application {
|
||||
|
||||
override fun start(state: Map<String, Any>) {
|
||||
|
||||
injectGlobal {
|
||||
body {
|
||||
height = 100.pct
|
||||
width = 100.pct
|
||||
margin(0.px)
|
||||
padding(0.px)
|
||||
}
|
||||
}
|
||||
|
||||
val context = Global.context("demo") {}
|
||||
val element = document.getElementById("app") ?: error("Element with id 'app' not found on page")
|
||||
|
||||
render(element) {
|
||||
child(GDMLApp) {
|
||||
attrs {
|
||||
this.context = context
|
||||
this.rootObject = cubes().toVision(gdmlConfiguration)
|
||||
}
|
||||
}
|
||||
}
|
||||
// (document.getElementById("file_load_button") as? HTMLInputElement)?.apply {
|
||||
// addEventListener("change", {
|
||||
// (it.target as HTMLInputElement).files?.asList()?.first()?.let { file ->
|
||||
// FileReader().apply {
|
||||
// onload = {
|
||||
// val string = result as String
|
||||
// action(file.name, string)
|
||||
// }
|
||||
// readAsText(file)
|
||||
// }
|
||||
// }
|
||||
// }, false)
|
||||
// }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
startApplication(::GDMLDemoApp)
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package space.kscience.visionforge.gdml.demo
|
||||
|
||||
import kotlinx.browser.window
|
||||
import org.w3c.files.FileReader
|
||||
import org.w3c.files.get
|
||||
import react.*
|
||||
import react.dom.h2
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.context.fetch
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.gdml.Gdml
|
||||
import space.kscience.gdml.decodeFromString
|
||||
import space.kscience.visionforge.gdml.markLayers
|
||||
import space.kscience.visionforge.gdml.toVision
|
||||
import space.kscience.visionforge.ring.ThreeCanvasWithControls
|
||||
import space.kscience.visionforge.ring.tab
|
||||
import space.kscience.visionforge.root
|
||||
import space.kscience.visionforge.solid.Solid
|
||||
import space.kscience.visionforge.solid.Solids
|
||||
|
||||
external interface GDMLAppProps : RProps {
|
||||
var context: Context
|
||||
var vision: Solid?
|
||||
var selected: Name?
|
||||
}
|
||||
|
||||
@JsExport
|
||||
val GDMLApp = functionalComponent<GDMLAppProps>("GDMLApp") { props ->
|
||||
val visionManager = useMemo(props.context) { props.context.fetch(Solids).visionManager }
|
||||
var vision: Solid? by useState { props.vision?.apply { root(visionManager) } }
|
||||
|
||||
fun loadData(name: String, data: String) {
|
||||
val parsedVision = when {
|
||||
name.endsWith(".gdml") || name.endsWith(".xml") -> {
|
||||
val gdml = Gdml.decodeFromString(data)
|
||||
gdml.toVision().apply {
|
||||
root(visionManager)
|
||||
console.info("Marking layers for file $name")
|
||||
markLayers()
|
||||
}
|
||||
}
|
||||
name.endsWith(".json") -> visionManager.decodeFromString(data)
|
||||
else -> {
|
||||
window.alert("File extension is not recognized: $name")
|
||||
error("File extension is not recognized: $name")
|
||||
}
|
||||
}
|
||||
|
||||
vision = parsedVision as? Solid ?: error("Parsed vision is not a solid")
|
||||
}
|
||||
|
||||
child(ThreeCanvasWithControls) {
|
||||
attrs {
|
||||
this.context = props.context
|
||||
this.solid = vision
|
||||
this.selected = props.selected
|
||||
tab("Load") {
|
||||
h2 {
|
||||
+"Drag and drop .gdml or .json VisionForge files here"
|
||||
}
|
||||
fileDrop("(drag file here)") { files ->
|
||||
val file = files?.get(0)
|
||||
if (file != null) {
|
||||
FileReader().apply {
|
||||
onload = {
|
||||
val string = result as String
|
||||
loadData(file.name, string)
|
||||
}
|
||||
readAsText(file)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package space.kscience.visionforge.gdml.demo
|
||||
|
||||
import kotlinx.browser.document
|
||||
import kotlinx.css.*
|
||||
import react.child
|
||||
import react.dom.render
|
||||
import space.kscience.dataforge.context.Global
|
||||
import space.kscience.gdml.GdmlShowCase
|
||||
import space.kscience.visionforge.Application
|
||||
import space.kscience.visionforge.gdml.toVision
|
||||
import space.kscience.visionforge.solid.three.ThreePlugin
|
||||
import space.kscience.visionforge.startApplication
|
||||
import styled.injectGlobal
|
||||
|
||||
|
||||
private class GDMLDemoApp : Application {
|
||||
|
||||
override fun start(state: Map<String, Any>) {
|
||||
val context = Global.buildContext("gdml-demo"){
|
||||
plugin(ThreePlugin)
|
||||
}
|
||||
|
||||
injectGlobal {
|
||||
html{
|
||||
height = 100.pct
|
||||
}
|
||||
|
||||
body{
|
||||
height = 100.pct
|
||||
display = Display.flex
|
||||
alignItems = Align.stretch
|
||||
}
|
||||
|
||||
"#application"{
|
||||
width = 100.pct
|
||||
display = Display.flex
|
||||
alignItems = Align.stretch
|
||||
}
|
||||
}
|
||||
|
||||
val element = document.getElementById("application") ?: error("Element with id 'application' not found on page")
|
||||
|
||||
render(element) {
|
||||
child(GDMLApp) {
|
||||
val vision = GdmlShowCase.cubes().toVision()
|
||||
//println(context.plugins.fetch(VisionManager).encodeToString(vision))
|
||||
attrs {
|
||||
this.context = context
|
||||
this.vision = vision
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
startApplication(::GDMLDemoApp)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package hep.dataforge.vision.gdml.demo
|
||||
package space.kscience.visionforge.gdml.demo
|
||||
|
||||
import drop.FileDrop
|
||||
import kotlinx.css.*
|
||||
@@ -14,6 +14,7 @@ fun RBuilder.fileDrop(title: String, action: (files: FileList?) -> Unit) {
|
||||
styledDiv {
|
||||
css {
|
||||
border(style = BorderStyle.dashed, width = 1.px, color = Color.orange)
|
||||
flexGrow = 0.0
|
||||
alignContent = Align.center
|
||||
}
|
||||
|
||||
@@ -2,16 +2,12 @@
|
||||
<html>
|
||||
<head>
|
||||
<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">
|
||||
<title>Three js demo for particle physics</title>
|
||||
<link rel="stylesheet" href="css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="css/main.css">
|
||||
<script type="text/javascript" src="gdml.js"></script>
|
||||
<link rel="stylesheet" href="css/fileDrop.css">
|
||||
<script type="text/javascript" src="main.bundle.js"></script>
|
||||
<script type="text/javascript" src ="js/jquery-3.4.1.min.js"></script>
|
||||
<script type="text/javascript" src ="js/bootstrap.bundle.min.js"></script>
|
||||
</head>
|
||||
<body class="application">
|
||||
<div class="container-fluid" id = "app"> </div>
|
||||
<div id = "application"></div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,54 +0,0 @@
|
||||
package hep.dataforge.vision.gdml.demo
|
||||
|
||||
import hep.dataforge.meta.DFExperimental
|
||||
import hep.dataforge.meta.setItem
|
||||
import hep.dataforge.values.asValue
|
||||
import hep.dataforge.vision.gdml.LUnit
|
||||
import hep.dataforge.vision.gdml.readFile
|
||||
import hep.dataforge.vision.gdml.toVision
|
||||
import hep.dataforge.vision.solid.SolidGroup
|
||||
import hep.dataforge.vision.solid.SolidManager
|
||||
import hep.dataforge.vision.solid.SolidMaterial
|
||||
import scientifik.gdml.GDML
|
||||
import java.io.File
|
||||
import java.util.zip.GZIPInputStream
|
||||
import java.util.zip.ZipInputStream
|
||||
|
||||
@OptIn(DFExperimental::class)
|
||||
fun SolidManager.Companion.readFile(file: File): SolidGroup = when {
|
||||
file.extension == "gdml" || file.extension == "xml" -> {
|
||||
GDML.readFile(file.toPath()).toVision {
|
||||
lUnit = LUnit.CM
|
||||
|
||||
solidConfiguration = { parent, solid ->
|
||||
if (solid.name == "cave") {
|
||||
setItem(SolidMaterial.MATERIAL_WIREFRAME_KEY, true.asValue())
|
||||
}
|
||||
if (parent.physVolumes.isNotEmpty()) {
|
||||
useStyle("opaque") {
|
||||
SolidMaterial.MATERIAL_OPACITY_KEY put 0.3
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
file.extension == "json" -> SolidGroup.parseJson(file.readText())
|
||||
file.name.endsWith("json.zip") -> {
|
||||
file.inputStream().use {
|
||||
val unzip = ZipInputStream(it, Charsets.UTF_8)
|
||||
val text = unzip.readBytes().decodeToString()
|
||||
SolidGroup.parseJson(text)
|
||||
}
|
||||
}
|
||||
file.name.endsWith("json.gz") -> {
|
||||
file.inputStream().use {
|
||||
val unzip = GZIPInputStream(it)
|
||||
val text = unzip.readBytes().decodeToString()
|
||||
SolidGroup.parseJson(text)
|
||||
}
|
||||
}
|
||||
else -> error("Unknown extension ${file.extension}")
|
||||
}
|
||||
|
||||
@OptIn(DFExperimental::class)
|
||||
fun SolidManager.Companion.readFile(fileName: String): SolidGroup = readFile(File(fileName))
|
||||
@@ -1,28 +0,0 @@
|
||||
package hep.dataforge.vision.gdml.demo
|
||||
|
||||
import hep.dataforge.vision.gdml.LUnit
|
||||
import hep.dataforge.vision.gdml.readFile
|
||||
import hep.dataforge.vision.gdml.toVision
|
||||
import hep.dataforge.vision.solid.stringify
|
||||
import scientifik.gdml.GDML
|
||||
import java.io.File
|
||||
import java.nio.file.Paths
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
require(args.isNotEmpty()){"At least one argument is required"}
|
||||
val inputFileName = args[0]
|
||||
require(inputFileName.endsWith(".gdml")){"GDML required"}
|
||||
val outputFileName = args.getOrNull(1)?:inputFileName.replace(".gdml",".json")
|
||||
|
||||
val gdml = GDML.readFile(Paths.get(inputFileName))
|
||||
//GDML.readFile(Paths.get("D:\\Work\\Projects\\visionforge\\visionforge-spatial-gdml\\src\\jvmTest\\resources\\gdml\\simple1.gdml"))
|
||||
|
||||
val visual = gdml.toVision {
|
||||
lUnit = LUnit.CM
|
||||
}
|
||||
|
||||
val json = visual.stringify()
|
||||
println(json)
|
||||
File(outputFileName).writeText(json)
|
||||
//File("D:\\Work\\Projects\\gdml.kt\\gdml-source\\cubes.json").writeText(json)
|
||||
}
|
||||
@@ -1,22 +1,32 @@
|
||||
package hep.dataforge.vision.gdml.demo
|
||||
package space.kscience.visionforge.gdml.demo
|
||||
|
||||
import hep.dataforge.context.Global
|
||||
import hep.dataforge.vision.editor.VisualObjectEditorFragment
|
||||
import hep.dataforge.vision.editor.VisualObjectTreeFragment
|
||||
import hep.dataforge.vision.gdml.toVision
|
||||
import hep.dataforge.vision.solid.SolidManager
|
||||
import hep.dataforge.vision.solid.SolidMaterial
|
||||
import hep.dataforge.vision.solid.fx.FX3DPlugin
|
||||
import hep.dataforge.vision.solid.fx.FXCanvas3D
|
||||
import javafx.geometry.Orientation
|
||||
import javafx.scene.Parent
|
||||
import javafx.stage.FileChooser
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.context.fetch
|
||||
import space.kscience.gdml.GdmlShowCase
|
||||
import space.kscience.visionforge.VisionManager
|
||||
import space.kscience.visionforge.describedProperties
|
||||
import space.kscience.visionforge.editor.VisualObjectEditorFragment
|
||||
import space.kscience.visionforge.editor.VisualObjectTreeFragment
|
||||
import space.kscience.visionforge.gdml.toVision
|
||||
import space.kscience.visionforge.solid.FX3DPlugin
|
||||
import space.kscience.visionforge.solid.FXCanvas3D
|
||||
import space.kscience.visionforge.solid.Solid
|
||||
import space.kscience.visionforge.solid.SolidMaterial
|
||||
import tornadofx.*
|
||||
|
||||
class GDMLDemoApp : App(GDMLView::class)
|
||||
|
||||
class GDMLView : View() {
|
||||
private val fx3d = Global.plugins.fetch(FX3DPlugin)
|
||||
private val context = Context {
|
||||
plugin(FX3DPlugin)
|
||||
plugin(VisionManager)
|
||||
}
|
||||
|
||||
private val fx3d = context.fetch(FX3DPlugin)
|
||||
private val visionManager = context.fetch(VisionManager)
|
||||
private val canvas = FXCanvas3D(fx3d)
|
||||
|
||||
private val treeFragment = VisualObjectTreeFragment().apply {
|
||||
@@ -24,7 +34,7 @@ class GDMLView : View() {
|
||||
}
|
||||
|
||||
private val propertyEditor = VisualObjectEditorFragment {
|
||||
it.getAllProperties()
|
||||
it.describedProperties
|
||||
}.apply {
|
||||
descriptorProperty.set(SolidMaterial.descriptor)
|
||||
itemProperty.bind(treeFragment.selectedProperty)
|
||||
@@ -36,12 +46,11 @@ class GDMLView : View() {
|
||||
buttonbar {
|
||||
button("Load GDML/json") {
|
||||
action {
|
||||
runAsync {
|
||||
val file = chooseFile("Select a GDML/json file", filters = fileNameFilter).firstOrNull()
|
||||
?: return@runAsync null
|
||||
SolidManager.readFile(file)
|
||||
} ui {
|
||||
if (it != null) {
|
||||
val file = chooseFile("Select a GDML/json file", filters = fileNameFilter).firstOrNull()
|
||||
if(file!= null) {
|
||||
runAsync {
|
||||
visionManager.readFile(file) as Solid
|
||||
} ui {
|
||||
canvas.render(it)
|
||||
}
|
||||
}
|
||||
@@ -58,7 +67,7 @@ class GDMLView : View() {
|
||||
|
||||
init {
|
||||
runAsync {
|
||||
cubes().toVision()
|
||||
GdmlShowCase.cubes().toVision()
|
||||
} ui {
|
||||
canvas.render(it)
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package space.kscience.visionforge.gdml.demo
|
||||
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.gdml.Gdml
|
||||
import space.kscience.gdml.decodeFromFile
|
||||
import space.kscience.visionforge.Vision
|
||||
import space.kscience.visionforge.VisionManager
|
||||
import space.kscience.visionforge.gdml.toVision
|
||||
import java.io.File
|
||||
import java.util.zip.GZIPInputStream
|
||||
import java.util.zip.ZipInputStream
|
||||
|
||||
@OptIn(DFExperimental::class)
|
||||
fun VisionManager.readFile(file: File): Vision = when {
|
||||
file.extension == "gdml" || file.extension == "xml" -> {
|
||||
Gdml.decodeFromFile(file.toPath(),true).toVision()
|
||||
}
|
||||
file.extension == "json" -> decodeFromString(file.readText())
|
||||
file.name.endsWith("json.zip") -> {
|
||||
file.inputStream().use {
|
||||
val unzip = ZipInputStream(it, Charsets.UTF_8)
|
||||
val text = unzip.readBytes().decodeToString()
|
||||
decodeFromString(text)
|
||||
}
|
||||
}
|
||||
file.name.endsWith("json.gz") -> {
|
||||
file.inputStream().use {
|
||||
val unzip = GZIPInputStream(it)
|
||||
val text = unzip.readBytes().decodeToString()
|
||||
decodeFromString(text)
|
||||
}
|
||||
}
|
||||
else -> error("Unknown extension ${file.extension}")
|
||||
}
|
||||
|
||||
@OptIn(DFExperimental::class)
|
||||
fun VisionManager.readFile(fileName: String): Vision = readFile(File(fileName))
|
||||
@@ -0,0 +1,28 @@
|
||||
package space.kscience.visionforge.gdml.demo
|
||||
|
||||
import space.kscience.gdml.Gdml
|
||||
import space.kscience.gdml.LUnit
|
||||
import space.kscience.gdml.decodeFromFile
|
||||
import space.kscience.visionforge.gdml.toVision
|
||||
import space.kscience.visionforge.solid.Solids
|
||||
import java.io.File
|
||||
import java.nio.file.Paths
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
require(args.isNotEmpty()) { "At least one argument is required" }
|
||||
val inputFileName = args[0]
|
||||
require(inputFileName.endsWith(".gdml")) { "GDML required" }
|
||||
val outputFileName = args.getOrNull(1) ?: inputFileName.replace(".gdml", ".json")
|
||||
|
||||
val gdml = Gdml.decodeFromFile(Paths.get(inputFileName), true)
|
||||
//GDML.readFile(Paths.get("D:\\Work\\Projects\\visionforge\\visionforge-spatial-gdml\\src\\jvmTest\\resources\\gdml\\simple1.gdml"))
|
||||
|
||||
val vision = gdml.toVision {
|
||||
lUnit = LUnit.CM
|
||||
}
|
||||
|
||||
val json = Solids.encodeToString(vision)
|
||||
println(json)
|
||||
File(outputFileName).writeText(json)
|
||||
//File("D:\\Work\\Projects\\gdml.kt\\gdml-source\\cubes.json").writeText(json)
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package hep.dataforge.vision.solid
|
||||
package space.kscience.visionforge.solid
|
||||
|
||||
import hep.dataforge.names.asName
|
||||
import org.junit.jupiter.api.Test
|
||||
import space.kscience.dataforge.names.asName
|
||||
import kotlin.test.Ignore
|
||||
|
||||
class FileSerializationTest {
|
||||
@@ -9,7 +9,7 @@ class FileSerializationTest {
|
||||
@Ignore
|
||||
fun testFileRead(){
|
||||
val text = this::class.java.getResourceAsStream("/cubes.json").readBytes().decodeToString()
|
||||
val visual = SolidGroup.parseJson(text)
|
||||
val visual = Solids.decodeFromString(text) as SolidGroup
|
||||
visual["composite_001".asName()]
|
||||
}
|
||||
}
|
||||
3
demo/gdml/webpack.config.d/01.ring.js
vendored
Normal file
3
demo/gdml/webpack.config.d/01.ring.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
const ringConfig = require('@jetbrains/ring-ui/webpack.config').config;
|
||||
|
||||
config.module.rules.push(...ringConfig.module.rules)
|
||||
27
demo/js-playground/build.gradle.kts
Normal file
27
demo/js-playground/build.gradle.kts
Normal file
@@ -0,0 +1,27 @@
|
||||
plugins {
|
||||
id("ru.mipt.npm.gradle.js")
|
||||
}
|
||||
|
||||
kscience{
|
||||
useCoroutines()
|
||||
application()
|
||||
}
|
||||
|
||||
kotlin{
|
||||
js(IR){
|
||||
useCommonJs()
|
||||
browser {
|
||||
commonWebpackConfig {
|
||||
cssSupport.enabled = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
dependencies{
|
||||
implementation(project(":visionforge-gdml"))
|
||||
implementation(project(":visionforge-plotly"))
|
||||
implementation(project(":visionforge-threejs"))
|
||||
implementation(project(":ui:ring"))
|
||||
}
|
||||
50
demo/js-playground/src/main/kotlin/JsPlaygroundApp.kt
Normal file
50
demo/js-playground/src/main/kotlin/JsPlaygroundApp.kt
Normal file
@@ -0,0 +1,50 @@
|
||||
import kotlinx.browser.document
|
||||
import kotlinx.css.*
|
||||
import react.child
|
||||
import react.dom.render
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.gdml.GdmlShowCase
|
||||
import space.kscience.visionforge.Application
|
||||
import space.kscience.visionforge.VisionClient
|
||||
import space.kscience.visionforge.gdml.toVision
|
||||
import space.kscience.visionforge.ring.ThreeCanvasWithControls
|
||||
import space.kscience.visionforge.ring.ThreeWithControlsPlugin
|
||||
import space.kscience.visionforge.startApplication
|
||||
import styled.css
|
||||
import styled.styledDiv
|
||||
|
||||
private class JsPlaygroundApp : Application {
|
||||
|
||||
override fun start(state: Map<String, Any>) {
|
||||
|
||||
val playgroundContext = Context {
|
||||
plugin(ThreeWithControlsPlugin)
|
||||
plugin(VisionClient)
|
||||
}
|
||||
|
||||
val element = document.getElementById("playground") ?: error("Element with id 'playground' not found on page")
|
||||
|
||||
val visionOfD0 = GdmlShowCase.babyIaxo().toVision()
|
||||
|
||||
render(element) {
|
||||
styledDiv {
|
||||
css{
|
||||
padding(0.pt)
|
||||
margin(0.pt)
|
||||
height = 100.vh
|
||||
width = 100.vw
|
||||
}
|
||||
child(ThreeCanvasWithControls) {
|
||||
attrs {
|
||||
context = playgroundContext
|
||||
solid = visionOfD0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public fun main() {
|
||||
startApplication(::JsPlaygroundApp)
|
||||
}
|
||||
12
demo/js-playground/src/main/resources/index.html
Normal file
12
demo/js-playground/src/main/resources/index.html
Normal file
@@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>js-playground</title>
|
||||
<script src="js-playground.js"></script>
|
||||
</head>
|
||||
<body class="application">
|
||||
<div id="playground"></div>
|
||||
</body>
|
||||
</html>
|
||||
3
demo/js-playground/webpack.config.d/01.ring.js
vendored
Normal file
3
demo/js-playground/webpack.config.d/01.ring.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
const ringConfig = require('@jetbrains/ring-ui/webpack.config').config;
|
||||
|
||||
config.module.rules.push(...ringConfig.module.rules)
|
||||
35
demo/jupyter-playground/build.gradle.kts
Normal file
35
demo/jupyter-playground/build.gradle.kts
Normal file
@@ -0,0 +1,35 @@
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
kotlin("jupyter.api")
|
||||
id("com.github.johnrengelman.shadow") version "6.1.0"
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven("https://repo.kotlin.link")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":demo:playground"))
|
||||
}
|
||||
|
||||
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile> {
|
||||
kotlinOptions {
|
||||
useIR = true
|
||||
jvmTarget = ru.mipt.npm.gradle.KScienceVersions.JVM_TARGET.toString()
|
||||
}
|
||||
}
|
||||
|
||||
extensions.findByType<JavaPluginExtension>()?.apply {
|
||||
targetCompatibility = ru.mipt.npm.gradle.KScienceVersions.JVM_TARGET
|
||||
}
|
||||
|
||||
tasks.withType<Test> {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
tasks.processJupyterApiResources {
|
||||
libraryProducers = listOf("space.kscience.dataforge.playground.VisionForgePlayGroundForJupyter")
|
||||
}
|
||||
|
||||
tasks.findByName("shadowJar")?.dependsOn("processJupyterApiResources")
|
||||
@@ -0,0 +1,98 @@
|
||||
package space.kscience.dataforge.playground
|
||||
|
||||
import kotlinx.html.div
|
||||
import kotlinx.html.id
|
||||
import kotlinx.html.script
|
||||
import kotlinx.html.stream.createHTML
|
||||
import kotlinx.html.unsafe
|
||||
import org.jetbrains.kotlinx.jupyter.api.HTML
|
||||
import org.jetbrains.kotlinx.jupyter.api.annotations.JupyterLibrary
|
||||
import org.jetbrains.kotlinx.jupyter.api.libraries.*
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.gdml.Gdml
|
||||
import space.kscience.plotly.Plot
|
||||
import space.kscience.visionforge.Vision
|
||||
import space.kscience.visionforge.gdml.toVision
|
||||
import space.kscience.visionforge.html.HtmlVisionFragment
|
||||
import space.kscience.visionforge.html.Page
|
||||
import space.kscience.visionforge.html.embedVisionFragment
|
||||
import space.kscience.visionforge.plotly.PlotlyPlugin
|
||||
import space.kscience.visionforge.plotly.asVision
|
||||
import space.kscience.visionforge.solid.Solids
|
||||
import space.kscience.visionforge.visionManager
|
||||
|
||||
@JupyterLibrary
|
||||
@DFExperimental
|
||||
internal class VisionForgePlayGroundForJupyter : JupyterIntegration() {
|
||||
|
||||
private val context = Context("VisionForge") {
|
||||
plugin(Solids)
|
||||
plugin(PlotlyPlugin)
|
||||
}
|
||||
|
||||
private val jsBundle = ResourceFallbacksBundle(listOf(
|
||||
ResourceLocation("js/visionforge-playground.js", ResourcePathType.CLASSPATH_PATH))
|
||||
)
|
||||
private val jsResource = LibraryResource(name = "VisionForge", type = ResourceType.JS, bundles = listOf(jsBundle))
|
||||
|
||||
private var counter = 0
|
||||
|
||||
private fun produceHtmlVisionString(fragment: HtmlVisionFragment) = createHTML().div {
|
||||
val id = "visionforge.vision[${counter++}]"
|
||||
div {
|
||||
this.id = id
|
||||
embedVisionFragment(context.visionManager, fragment = fragment)
|
||||
}
|
||||
script {
|
||||
type = "text/javascript"
|
||||
unsafe { +"window.renderAllVisionsById(\"$id\");" }
|
||||
}
|
||||
}
|
||||
|
||||
override fun Builder.onLoaded() {
|
||||
resource(jsResource)
|
||||
|
||||
import(
|
||||
"space.kscience.gdml.*",
|
||||
"space.kscience.plotly.*",
|
||||
"space.kscience.plotly.models.*",
|
||||
"kotlinx.html.*",
|
||||
"space.kscience.visionforge.solid.*",
|
||||
"space.kscience.visionforge.html.Page",
|
||||
"space.kscience.visionforge.html.page"
|
||||
)
|
||||
|
||||
render<Gdml> { gdmlModel ->
|
||||
val fragment = HtmlVisionFragment {
|
||||
vision(gdmlModel.toVision())
|
||||
}
|
||||
HTML(produceHtmlVisionString(fragment))
|
||||
}
|
||||
|
||||
render<Vision> { vision ->
|
||||
val fragment = HtmlVisionFragment {
|
||||
vision(vision)
|
||||
}
|
||||
|
||||
HTML(produceHtmlVisionString(fragment))
|
||||
}
|
||||
|
||||
render<Plot> { plot ->
|
||||
val fragment = HtmlVisionFragment {
|
||||
vision(plot.asVision())
|
||||
}
|
||||
|
||||
HTML(produceHtmlVisionString(fragment))
|
||||
}
|
||||
|
||||
render<space.kscience.plotly.PlotlyHtmlFragment> { fragment ->
|
||||
HTML(createHTML().apply(fragment.visit).finalize())
|
||||
}
|
||||
|
||||
render<Page> { page ->
|
||||
HTML(page.render(createHTML()), true)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -30,5 +30,5 @@ run `demo/muon-monitor/application/run` task.
|
||||
|
||||
##### Example view:
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
@@ -1,43 +1,31 @@
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.Companion.MAIN_COMPILATION_NAME
|
||||
import scientifik.jsDistDirectory
|
||||
|
||||
plugins {
|
||||
id("scientifik.mpp")
|
||||
id("application")
|
||||
id("ru.mipt.npm.gradle.mpp")
|
||||
application
|
||||
}
|
||||
|
||||
group = "ru.mipt.npm"
|
||||
|
||||
val ktorVersion = "1.3.2"
|
||||
val ktorVersion: String = ru.mipt.npm.gradle.KScienceVersions.ktorVersion
|
||||
|
||||
kscience {
|
||||
useCoroutines()
|
||||
useSerialization()
|
||||
application()
|
||||
}
|
||||
|
||||
kotlin {
|
||||
|
||||
val installJS = tasks.getByName("jsBrowserDistribution")
|
||||
|
||||
js {
|
||||
browser {
|
||||
dceTask {
|
||||
dceOptions {
|
||||
keep("ktor-ktor-io.\$\$importsForInline\$\$.ktor-ktor-io.io.ktor.utils.io")
|
||||
}
|
||||
}
|
||||
webpackTask {
|
||||
mode = org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig.Mode.PRODUCTION
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jvm {
|
||||
withJava()
|
||||
compilations[MAIN_COMPILATION_NAME]?.apply {
|
||||
tasks.getByName<ProcessResources>(processResourcesTaskName) {
|
||||
dependsOn(installJS)
|
||||
afterEvaluate {
|
||||
from(project.jsDistDirectory)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
val jsBrowserDistribution by tasks.getting
|
||||
|
||||
tasks.getByName<ProcessResources>("jvmProcessResources") {
|
||||
dependsOn(jsBrowserDistribution)
|
||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||
from(jsBrowserDistribution)
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
@@ -57,31 +45,31 @@ kotlin {
|
||||
dependencies {
|
||||
implementation(project(":ui:bootstrap"))
|
||||
implementation("io.ktor:ktor-client-js:$ktorVersion")
|
||||
implementation("io.ktor:ktor-client-serialization-js:$ktorVersion")
|
||||
implementation(npm("text-encoding"))
|
||||
implementation(npm("abort-controller"))
|
||||
implementation(npm("bufferutil"))
|
||||
implementation(npm("utf-8-validate"))
|
||||
implementation(npm("fs"))
|
||||
// implementation(npm("jquery"))
|
||||
// implementation(npm("popper.js"))
|
||||
// implementation(npm("react-is"))
|
||||
implementation("io.ktor:ktor-client-serialization:$ktorVersion")
|
||||
implementation(project(":visionforge-threejs"))
|
||||
implementation(devNpm("webpack-bundle-analyzer", "4.4.0"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
application {
|
||||
mainClassName = "ru.mipt.npm.muon.monitor.server.MMServerKt"
|
||||
mainClass.set("ru.mipt.npm.muon.monitor.server.MMServerKt")
|
||||
}
|
||||
|
||||
distributions {
|
||||
main {
|
||||
contents {
|
||||
from("$buildDir/libs") {
|
||||
rename("${rootProject.name}-jvm", rootProject.name)
|
||||
into("lib")
|
||||
}
|
||||
}
|
||||
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinJsCompile>() {
|
||||
kotlinOptions {
|
||||
freeCompilerArgs = freeCompilerArgs + "-Xir-property-lazy-initialization"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//distributions {
|
||||
// main {
|
||||
// contents {
|
||||
// from("$buildDir/libs") {
|
||||
// rename("${rootProject.name}-jvm", rootProject.name)
|
||||
// into("lib")
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
@@ -1,10 +1,7 @@
|
||||
@file:UseSerializers(Point3DSerializer::class)
|
||||
package ru.mipt.npm.muon.monitor
|
||||
|
||||
import hep.dataforge.vision.solid.Point3D
|
||||
import hep.dataforge.vision.solid.Point3DSerializer
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.UseSerializers
|
||||
import space.kscience.visionforge.solid.Point3D
|
||||
|
||||
typealias Track = List<Point3D>
|
||||
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
package ru.mipt.npm.muon.monitor
|
||||
|
||||
import hep.dataforge.vision.removeAll
|
||||
import hep.dataforge.vision.solid.*
|
||||
import ru.mipt.npm.muon.monitor.Monitor.CENTRAL_LAYER_Z
|
||||
import ru.mipt.npm.muon.monitor.Monitor.LOWER_LAYER_Z
|
||||
import ru.mipt.npm.muon.monitor.Monitor.UPPER_LAYER_Z
|
||||
import space.kscience.visionforge.VisionManager
|
||||
import space.kscience.visionforge.removeAll
|
||||
import space.kscience.visionforge.root
|
||||
import space.kscience.visionforge.solid.*
|
||||
import kotlin.math.PI
|
||||
|
||||
class Model {
|
||||
class Model(val manager: VisionManager) {
|
||||
private val map = HashMap<String, SolidGroup>()
|
||||
private val events = HashSet<Event>()
|
||||
|
||||
@@ -34,6 +36,7 @@ class Model {
|
||||
var tracks: SolidGroup
|
||||
|
||||
val root: SolidGroup = SolidGroup().apply {
|
||||
root(this@Model.manager)
|
||||
rotationX = PI / 2
|
||||
group("bottom") {
|
||||
Monitor.detectors.filter { it.center.z == LOWER_LAYER_Z }.forEach {
|
||||
@@ -52,18 +55,16 @@ class Model {
|
||||
detector(it)
|
||||
}
|
||||
}
|
||||
|
||||
tracks = group("tracks")
|
||||
}
|
||||
|
||||
private fun highlight(pixel: String) {
|
||||
map[pixel]?.color("blue")
|
||||
map[pixel]?.color?.invoke("blue")
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
map.values.forEach {
|
||||
it.config
|
||||
it.setItem(SolidMaterial.MATERIAL_COLOR_KEY, null)
|
||||
it.setProperty(SolidMaterial.MATERIAL_COLOR_KEY, null)
|
||||
}
|
||||
tracks.removeAll()
|
||||
}
|
||||
@@ -80,7 +81,5 @@ class Model {
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun buildGeometry() = Model().root
|
||||
}
|
||||
fun encodeToString(): String = manager.encodeToString(this.root)
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
package ru.mipt.npm.muon.monitor
|
||||
|
||||
import hep.dataforge.vision.solid.Point3D
|
||||
import hep.dataforge.vision.solid.plus
|
||||
import ru.mipt.npm.muon.monitor.Monitor.PIXEL_XY_SIZE
|
||||
import ru.mipt.npm.muon.monitor.Monitor.PIXEL_Z_SIZE
|
||||
import space.kscience.visionforge.solid.Point3D
|
||||
import space.kscience.visionforge.solid.plus
|
||||
|
||||
/**
|
||||
* A single pixel
|
||||
@@ -11,7 +11,7 @@ import ru.mipt.npm.muon.monitor.Monitor.PIXEL_Z_SIZE
|
||||
class SC1(
|
||||
val name: String,
|
||||
val center: Point3D,
|
||||
val xSize: Double = PIXEL_XY_SIZE, val ySize: Double = PIXEL_XY_SIZE, val zSize: Double = PIXEL_Z_SIZE
|
||||
val xSize: Float = PIXEL_XY_SIZE, val ySize: Float = PIXEL_XY_SIZE, val zSize: Float = PIXEL_Z_SIZE
|
||||
)
|
||||
|
||||
class SC16(
|
||||
@@ -121,12 +121,12 @@ internal expect fun readMonitorConfig(): String
|
||||
object Monitor {
|
||||
|
||||
const val GEOMETRY_TOLERANCE = 0.01
|
||||
const val PIXEL_XY_SIZE = 122.0
|
||||
const val PIXEL_XY_SPACING = 123.2
|
||||
const val PIXEL_Z_SIZE = 30.0
|
||||
const val CENTRAL_LAYER_Z = 0.0
|
||||
const val UPPER_LAYER_Z = -166.0
|
||||
const val LOWER_LAYER_Z = 180.0
|
||||
const val PIXEL_XY_SIZE = 122.0f
|
||||
const val PIXEL_XY_SPACING = 123.2f
|
||||
const val PIXEL_Z_SIZE = 30.0f
|
||||
const val CENTRAL_LAYER_Z = 0.0f
|
||||
const val UPPER_LAYER_Z = -166.0f
|
||||
const val LOWER_LAYER_Z = 180.0f
|
||||
|
||||
/**
|
||||
* Build map for the whole monitor
|
||||
|
||||
@@ -1,89 +1,115 @@
|
||||
package ru.mipt.npm.muon.monitor
|
||||
|
||||
import hep.dataforge.context.Context
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.NameToken
|
||||
import hep.dataforge.names.isEmpty
|
||||
import hep.dataforge.vision.Vision
|
||||
import hep.dataforge.vision.bootstrap.card
|
||||
import hep.dataforge.vision.bootstrap.objectTree
|
||||
import hep.dataforge.vision.react.component
|
||||
import hep.dataforge.vision.react.configEditor
|
||||
import hep.dataforge.vision.react.state
|
||||
import hep.dataforge.vision.solid.specifications.Camera
|
||||
import hep.dataforge.vision.solid.specifications.Canvas3DOptions
|
||||
import hep.dataforge.vision.solid.three.ThreeCanvas
|
||||
import hep.dataforge.vision.solid.three.ThreeCanvasComponent
|
||||
import hep.dataforge.vision.solid.three.canvasControls
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.request.get
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.css.*
|
||||
import kotlinx.html.js.onClickFunction
|
||||
import react.RProps
|
||||
import react.*
|
||||
import react.dom.*
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.NameToken
|
||||
import space.kscience.dataforge.names.isEmpty
|
||||
import space.kscience.dataforge.names.length
|
||||
import space.kscience.visionforge.Vision
|
||||
import space.kscience.visionforge.bootstrap.canvasControls
|
||||
import space.kscience.visionforge.bootstrap.card
|
||||
import space.kscience.visionforge.bootstrap.gridRow
|
||||
import space.kscience.visionforge.bootstrap.visionPropertyEditor
|
||||
import space.kscience.visionforge.react.ThreeCanvasComponent
|
||||
import space.kscience.visionforge.react.flexColumn
|
||||
import space.kscience.visionforge.react.visionTree
|
||||
import space.kscience.visionforge.solid.specifications.Camera
|
||||
import space.kscience.visionforge.solid.specifications.Canvas3DOptions
|
||||
import styled.css
|
||||
import styled.styledDiv
|
||||
import kotlin.math.PI
|
||||
|
||||
interface MMAppProps : RProps {
|
||||
external interface MMAppProps : RProps {
|
||||
var model: Model
|
||||
var context: Context
|
||||
var connection: HttpClient
|
||||
var selected: Name?
|
||||
}
|
||||
|
||||
private val canvasConfig = Canvas3DOptions {
|
||||
camera = Camera {
|
||||
distance = 2100.0
|
||||
latitude = PI / 6
|
||||
azimuth = PI + PI / 6
|
||||
}
|
||||
}
|
||||
@JsExport
|
||||
val MMApp = functionalComponent<MMAppProps>("Muon monitor") { props ->
|
||||
var selected by useState { props.selected }
|
||||
|
||||
val MMApp = component<MMAppProps> { props ->
|
||||
var selected by state { props.selected }
|
||||
var canvas: ThreeCanvas? by state { null }
|
||||
|
||||
val select: (Name?) -> Unit = {
|
||||
val onSelect: (Name?) -> Unit = {
|
||||
selected = it
|
||||
}
|
||||
|
||||
val visual = props.model.root
|
||||
|
||||
div("row") {
|
||||
h1("mx-auto") {
|
||||
+"Muon monitor demo"
|
||||
val mmOptions = useMemo {
|
||||
Canvas3DOptions {
|
||||
camera = Camera {
|
||||
distance = 2100.0
|
||||
latitude = PI / 6
|
||||
azimuth = PI + PI / 6
|
||||
}
|
||||
this.onSelect = onSelect
|
||||
}
|
||||
}
|
||||
div("row") {
|
||||
div("col-lg-3 px-0 overflow-auto") {
|
||||
|
||||
val root = props.model.root
|
||||
|
||||
gridRow {
|
||||
flexColumn {
|
||||
css {
|
||||
+"col-lg-3"
|
||||
+"order-lg-1"
|
||||
+"order-2"
|
||||
padding(0.px)
|
||||
overflowY = Overflow.auto
|
||||
height = 100.vh
|
||||
}
|
||||
//tree
|
||||
card("Object tree") {
|
||||
objectTree(visual, selected, select)
|
||||
css {
|
||||
flex(1.0, 1.0, FlexBasis.auto)
|
||||
}
|
||||
visionTree(root, selected, onSelect)
|
||||
}
|
||||
}
|
||||
div("col-lg-6") {
|
||||
flexColumn {
|
||||
css {
|
||||
+"col-lg-6"
|
||||
+"order-lg-2"
|
||||
+"order-1"
|
||||
height = 100.vh
|
||||
}
|
||||
h1("mx-auto page-header") {
|
||||
+"Muon monitor demo"
|
||||
}
|
||||
//canvas
|
||||
child(ThreeCanvasComponent::class) {
|
||||
|
||||
child(ThreeCanvasComponent) {
|
||||
attrs {
|
||||
this.context = props.context
|
||||
this.obj = visual
|
||||
this.options = canvasConfig
|
||||
this.solid = root
|
||||
this.selected = selected
|
||||
this.clickCallback = select
|
||||
this.canvasCallback = {
|
||||
canvas = it
|
||||
}
|
||||
this.options = mmOptions
|
||||
}
|
||||
}
|
||||
}
|
||||
div("col-lg-3") {
|
||||
div("row") {
|
||||
//settings
|
||||
canvas?.let {
|
||||
card("Canvas configuration") {
|
||||
canvasControls(it)
|
||||
}
|
||||
flexColumn {
|
||||
css {
|
||||
+"col-lg-3"
|
||||
+"order-3"
|
||||
padding(0.px)
|
||||
height = 100.vh
|
||||
}
|
||||
styledDiv {
|
||||
css {
|
||||
flex(0.0, 1.0, FlexBasis.zero)
|
||||
}
|
||||
//settings
|
||||
card("Canvas configuration") {
|
||||
canvasControls(mmOptions, root)
|
||||
}
|
||||
|
||||
card("Events") {
|
||||
button {
|
||||
+"Next"
|
||||
@@ -106,36 +132,37 @@ val MMApp = component<MMAppProps> { props ->
|
||||
}
|
||||
}
|
||||
}
|
||||
div("row") {
|
||||
div("container-fluid p-0") {
|
||||
nav {
|
||||
attrs {
|
||||
attributes["aria-label"] = "breadcrumb"
|
||||
}
|
||||
ol("breadcrumb") {
|
||||
li("breadcrumb-item") {
|
||||
button(classes = "btn btn-link p-0") {
|
||||
+"World"
|
||||
attrs {
|
||||
onClickFunction = {
|
||||
selected = hep.dataforge.names.Name.EMPTY
|
||||
}
|
||||
styledDiv {
|
||||
css {
|
||||
padding(0.px)
|
||||
}
|
||||
nav {
|
||||
attrs {
|
||||
attributes["aria-label"] = "breadcrumb"
|
||||
}
|
||||
ol("breadcrumb") {
|
||||
li("breadcrumb-item") {
|
||||
button(classes = "btn btn-link p-0") {
|
||||
+"World"
|
||||
attrs {
|
||||
onClickFunction = {
|
||||
selected = Name.EMPTY
|
||||
}
|
||||
}
|
||||
}
|
||||
if (selected != null) {
|
||||
val tokens = ArrayList<NameToken>(selected?.length ?: 1)
|
||||
selected?.tokens?.forEach { token ->
|
||||
tokens.add(token)
|
||||
val fullName = Name(tokens.toList())
|
||||
li("breadcrumb-item") {
|
||||
button(classes = "btn btn-link p-0") {
|
||||
+token.toString()
|
||||
attrs {
|
||||
onClickFunction = {
|
||||
console.log("Selected = $fullName")
|
||||
selected = fullName
|
||||
}
|
||||
}
|
||||
if (selected != null) {
|
||||
val tokens = ArrayList<NameToken>(selected?.length ?: 1)
|
||||
selected?.tokens?.forEach { token ->
|
||||
tokens.add(token)
|
||||
val fullName = Name(tokens.toList())
|
||||
li("breadcrumb-item") {
|
||||
button(classes = "btn btn-link p-0") {
|
||||
+token.toString()
|
||||
attrs {
|
||||
onClickFunction = {
|
||||
console.log("Selected = $fullName")
|
||||
selected = fullName
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -145,17 +172,20 @@ val MMApp = component<MMAppProps> { props ->
|
||||
}
|
||||
}
|
||||
}
|
||||
div("row") {
|
||||
styledDiv {
|
||||
css {
|
||||
overflowY = Overflow.auto
|
||||
}
|
||||
//properties
|
||||
card("Properties") {
|
||||
selected.let { selected ->
|
||||
val selectedObject: Vision? = when {
|
||||
selected == null -> null
|
||||
selected.isEmpty() -> visual
|
||||
else -> visual[selected]
|
||||
selected.isEmpty() -> root
|
||||
else -> root[selected]
|
||||
}
|
||||
if (selectedObject != null) {
|
||||
configEditor(selectedObject, default = selectedObject.getAllProperties(), key = selected)
|
||||
visionPropertyEditor(selectedObject, key = selected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,43 +1,42 @@
|
||||
package ru.mipt.npm.muon.monitor
|
||||
|
||||
import hep.dataforge.context.Global
|
||||
import hep.dataforge.js.Application
|
||||
import hep.dataforge.js.startApplication
|
||||
import hep.dataforge.vision.solid.SolidManager
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.features.json.JsonFeature
|
||||
import io.ktor.client.features.json.serializer.KotlinxSerializer
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.browser.document
|
||||
import react.child
|
||||
import react.dom.div
|
||||
import react.dom.render
|
||||
import kotlin.browser.document
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.context.Global
|
||||
import space.kscience.dataforge.context.fetch
|
||||
import space.kscience.visionforge.Application
|
||||
import space.kscience.visionforge.VisionManager
|
||||
import space.kscience.visionforge.bootstrap.useBootstrap
|
||||
import space.kscience.visionforge.startApplication
|
||||
|
||||
private class MMDemoApp : Application {
|
||||
|
||||
private val model = Model()
|
||||
private val visionManager = Global.fetch(VisionManager)
|
||||
private val model = Model(visionManager)
|
||||
|
||||
private val connection = HttpClient {
|
||||
install(JsonFeature) {
|
||||
serializer = KotlinxSerializer(Json(context = SolidManager.serialModule))
|
||||
serializer = KotlinxSerializer()
|
||||
}
|
||||
}
|
||||
|
||||
//TODO introduce react application
|
||||
|
||||
override fun start(state: Map<String, Any>) {
|
||||
useBootstrap()
|
||||
|
||||
val context = Global.context("demo") {}
|
||||
val element = document.getElementById("app") ?: error("Element with id 'app' not found on page")
|
||||
|
||||
val context = Context("demo")
|
||||
render(element) {
|
||||
div("container-fluid h-100") {
|
||||
child(MMApp) {
|
||||
attrs {
|
||||
model = this@MMDemoApp.model
|
||||
connection = this@MMDemoApp.connection
|
||||
this.context = context
|
||||
}
|
||||
child(MMApp) {
|
||||
attrs {
|
||||
this.model = this@MMDemoApp.model
|
||||
this.connection = this@MMDemoApp.connection
|
||||
this.context = context
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
package ru.mipt.npm.muon.monitor
|
||||
|
||||
import hep.dataforge.js.requireJS
|
||||
|
||||
actual fun readResource(path: String): String {
|
||||
return requireJS(path) as String
|
||||
return kotlinext.js.require(path) as String
|
||||
}
|
||||
|
||||
// TODO replace by resource
|
||||
|
||||
@@ -4,13 +4,10 @@
|
||||
<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="css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="css/main.css">
|
||||
<script type="text/javascript" src="main.bundle.js"></script>
|
||||
<script type="text/javascript" src ="js/jquery-3.4.1.min.js"></script>
|
||||
<script type="text/javascript" src ="js/bootstrap.bundle.min.js"></script>
|
||||
<script type="text/javascript" src="muon-monitor.js"></script>
|
||||
<link rel="stylesheet" href="css/custom-bootstrap.css">
|
||||
</head>
|
||||
<body class="application">
|
||||
<div class="container-fluid" id = "app"> </div>
|
||||
<div class="container-fluid max-vh-100" id = "app"> </div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,7 +1,6 @@
|
||||
package ru.mipt.npm.muon.monitor.server
|
||||
|
||||
|
||||
import hep.dataforge.vision.solid.SolidManager
|
||||
import io.ktor.application.Application
|
||||
import io.ktor.application.call
|
||||
import io.ktor.application.install
|
||||
@@ -9,34 +8,42 @@ import io.ktor.application.log
|
||||
import io.ktor.features.CallLogging
|
||||
import io.ktor.features.ContentNegotiation
|
||||
import io.ktor.features.DefaultHeaders
|
||||
import io.ktor.http.ContentType
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.http.content.resources
|
||||
import io.ktor.http.content.static
|
||||
import io.ktor.response.respond
|
||||
import io.ktor.response.respondText
|
||||
import io.ktor.routing.Routing
|
||||
import io.ktor.routing.get
|
||||
import io.ktor.serialization.json
|
||||
import io.ktor.server.cio.CIO
|
||||
import io.ktor.server.engine.embeddedServer
|
||||
import io.ktor.util.KtorExperimentalAPI
|
||||
import org.apache.commons.math3.random.JDKRandomGenerator
|
||||
import ru.mipt.npm.muon.monitor.Model
|
||||
import ru.mipt.npm.muon.monitor.sim.Cos2TrackGenerator
|
||||
import ru.mipt.npm.muon.monitor.sim.simulateOne
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.context.Global
|
||||
import space.kscience.dataforge.context.fetch
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.visionforge.solid.Solids
|
||||
import java.awt.Desktop
|
||||
import java.io.File
|
||||
import java.net.URI
|
||||
|
||||
private val generator = Cos2TrackGenerator(JDKRandomGenerator(223))
|
||||
|
||||
fun Application.module() {
|
||||
@OptIn(DFExperimental::class)
|
||||
fun Application.module(context: Context = Global) {
|
||||
val currentDir = File(".").absoluteFile
|
||||
environment.log.info("Current directory: $currentDir")
|
||||
|
||||
val solidManager = context.fetch(Solids)
|
||||
|
||||
install(DefaultHeaders)
|
||||
install(CallLogging)
|
||||
install(ContentNegotiation) {
|
||||
json(module = SolidManager.serialModule)
|
||||
json()
|
||||
}
|
||||
install(Routing) {
|
||||
get("/event") {
|
||||
@@ -44,7 +51,11 @@ fun Application.module() {
|
||||
call.respond(event)
|
||||
}
|
||||
get("/geometry") {
|
||||
call.respond(Model.buildGeometry())
|
||||
call.respondText(
|
||||
Model(solidManager.visionManager).encodeToString(),
|
||||
contentType = ContentType.Application.Json,
|
||||
status = HttpStatusCode.OK
|
||||
)
|
||||
}
|
||||
static("/") {
|
||||
resources()
|
||||
@@ -57,7 +68,6 @@ fun Application.module() {
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(KtorExperimentalAPI::class)
|
||||
fun main() {
|
||||
embeddedServer(CIO, 8080, host = "localhost", module = Application::module).start(wait = true)
|
||||
}
|
||||
@@ -17,19 +17,19 @@ import kotlin.random.Random
|
||||
internal class SC1Aux(val sc: SC1, var efficiency: Double = 1.0) {
|
||||
// val layer: Layer = findLayer(center.z);
|
||||
private val upLayer =
|
||||
findLayer(sc.center.z + sc.zSize / 2.0)//Layer("${name}_up", center.z + zSize / 2.0);
|
||||
findLayer(sc.center.z + sc.zSize / 2f)//Layer("${name}_up", center.z + zSize / 2.0);
|
||||
private val bottomLayer =
|
||||
findLayer(sc.center.z - sc.zSize / 2.0)//Layer("${name}_bottom", center.z - zSize / 2.0);
|
||||
findLayer(sc.center.z - sc.zSize / 2f)//Layer("${name}_bottom", center.z - zSize / 2.0);
|
||||
private val centralLayer = findLayer(sc.center.z)
|
||||
|
||||
private val center = Vector3D(sc.center.x, sc.center.y, sc.center.z)
|
||||
private val center = Vector3D(sc.center.x.toDouble(), sc.center.y.toDouble(), sc.center.z.toDouble())
|
||||
|
||||
private val sideLayers: Array<Plane> = arrayOf(
|
||||
Plane(center.add(Vector3D(PIXEL_XY_SIZE / 2, 0.0, 0.0)), Vector3D(1.0, 0.0, 0.0), GEOMETRY_TOLERANCE),
|
||||
Plane(center.add(Vector3D(-PIXEL_XY_SIZE / 2, 0.0, 0.0)), Vector3D(-1.0, 0.0, 0.0), GEOMETRY_TOLERANCE),
|
||||
Plane(center.add(Vector3D(0.0, PIXEL_XY_SIZE / 2, 0.0)), Vector3D(0.0, 1.0, 0.0), GEOMETRY_TOLERANCE),
|
||||
Plane(center.add(Vector3D(0.0, -PIXEL_XY_SIZE / 2, 0.0)), Vector3D(0.0, -1.0, 0.0), GEOMETRY_TOLERANCE)
|
||||
);
|
||||
Plane(center.add(Vector3D(PIXEL_XY_SIZE / 2.0, 0.0, 0.0)), Vector3D(1.0, 0.0, 0.0), GEOMETRY_TOLERANCE),
|
||||
Plane(center.add(Vector3D(-PIXEL_XY_SIZE / 2.0, 0.0, 0.0)), Vector3D(-1.0, 0.0, 0.0), GEOMETRY_TOLERANCE),
|
||||
Plane(center.add(Vector3D(0.0, PIXEL_XY_SIZE / 2.0, 0.0)), Vector3D(0.0, 1.0, 0.0), GEOMETRY_TOLERANCE),
|
||||
Plane(center.add(Vector3D(0.0, -PIXEL_XY_SIZE / 2.0, 0.0)), Vector3D(0.0, -1.0, 0.0), GEOMETRY_TOLERANCE)
|
||||
)
|
||||
|
||||
//TODO add efficiency
|
||||
private fun containsPoint(x: Double, y: Double, z: Double, tolerance: Double = GEOMETRY_TOLERANCE): Boolean {
|
||||
@@ -63,8 +63,8 @@ internal class SC1Aux(val sc: SC1, var efficiency: Double = 1.0) {
|
||||
* The layer number from up to bottom
|
||||
*/
|
||||
fun getLayerNumber(): Int {
|
||||
return when (this.center.z) {
|
||||
UPPER_LAYER_Z -> 1;
|
||||
return when (this.center.z.toFloat()) {
|
||||
UPPER_LAYER_Z -> 1
|
||||
CENTRAL_LAYER_Z -> 2;
|
||||
LOWER_LAYER_Z -> 3;
|
||||
else -> throw RuntimeException("Unknown layer");
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package ru.mipt.npm.muon.monitor.sim
|
||||
|
||||
import hep.dataforge.vision.solid.Point3D
|
||||
import org.apache.commons.math3.geometry.euclidean.threed.Line
|
||||
import org.apache.commons.math3.geometry.euclidean.threed.Plane
|
||||
import org.apache.commons.math3.geometry.euclidean.threed.Vector3D
|
||||
import ru.mipt.npm.muon.monitor.Monitor.CENTRAL_LAYER_Z
|
||||
import ru.mipt.npm.muon.monitor.Monitor.GEOMETRY_TOLERANCE
|
||||
import space.kscience.visionforge.solid.Point3D
|
||||
|
||||
/**
|
||||
* Created by darksnake on 11-May-16.
|
||||
@@ -45,7 +45,7 @@ fun makeTrack(start: Vector3D, direction: Vector3D): Line {
|
||||
fun makeTrack(x: Double, y: Double, theta: Double, phi: Double): Line {
|
||||
//TODO check angle definitions
|
||||
return makeTrack(
|
||||
Vector3D(x, y, CENTRAL_LAYER_Z),
|
||||
Vector3D(x, y, CENTRAL_LAYER_Z.toDouble()),
|
||||
Vector3D(phi, theta)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -7,19 +7,18 @@ import ru.mipt.npm.muon.monitor.Event
|
||||
import ru.mipt.npm.muon.monitor.Monitor
|
||||
import ru.mipt.npm.muon.monitor.SC1
|
||||
import ru.mipt.npm.muon.monitor.readResource
|
||||
import java.util.*
|
||||
|
||||
|
||||
// minimal track length in detector
|
||||
internal const val MINIMAL_TRACK_LENGTH = 10.0
|
||||
|
||||
|
||||
private val layerCache = HashMap<Double, Plane>()
|
||||
private val layerCache = HashMap<Float, Plane>()
|
||||
|
||||
fun findLayer(z: Double): Plane = layerCache.getOrPut(z) {
|
||||
fun findLayer(z: Float): Plane = layerCache.getOrPut(z) {
|
||||
Plane(
|
||||
Vector3D(0.0, 0.0, z), Vector3D(0.0, 0.0, 1.0),
|
||||
Monitor.GEOMETRY_TOLERANCE
|
||||
Vector3D(0.0, 0.0, z.toDouble()), Vector3D(0.0, 0.0, 1.0),
|
||||
Monitor.GEOMETRY_TOLERANCE.toDouble()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -28,8 +28,8 @@ interface TrackGenerator {
|
||||
*/
|
||||
class UniformTrackGenerator(
|
||||
override val rnd: RandomGenerator,
|
||||
val maxX: Double = 4 * PIXEL_XY_SIZE,
|
||||
val maxY: Double = 4 * PIXEL_XY_SIZE
|
||||
val maxX: Float = 4 * PIXEL_XY_SIZE,
|
||||
val maxY: Float = 4 * PIXEL_XY_SIZE
|
||||
) :
|
||||
TrackGenerator {
|
||||
override fun generate(): Line {
|
||||
@@ -44,8 +44,8 @@ class UniformTrackGenerator(
|
||||
class FixedAngleGenerator(
|
||||
override val rnd: RandomGenerator,
|
||||
val phi: Double, val theta: Double,
|
||||
val maxX: Double = 4 * PIXEL_XY_SIZE,
|
||||
val maxY: Double = 4 * PIXEL_XY_SIZE
|
||||
val maxX: Float = 4 * PIXEL_XY_SIZE,
|
||||
val maxY: Float = 4 * PIXEL_XY_SIZE
|
||||
) : TrackGenerator {
|
||||
override fun generate(): Line {
|
||||
val x = (1 - rnd.nextDouble() * 2.0) * maxX
|
||||
@@ -60,8 +60,8 @@ class FixedAngleGenerator(
|
||||
class Cos2TrackGenerator(
|
||||
override val rnd: RandomGenerator,
|
||||
val power: Double = 2.0,
|
||||
val maxX: Double = 4 * PIXEL_XY_SIZE,
|
||||
val maxY: Double = 4 * PIXEL_XY_SIZE
|
||||
val maxX: Float = 4 * PIXEL_XY_SIZE,
|
||||
val maxY: Float = 4 * PIXEL_XY_SIZE
|
||||
) :
|
||||
TrackGenerator {
|
||||
override fun generate(): Line {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package ru.mipt.npm.muon.monitor
|
||||
|
||||
import kotlin.test.*
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class GeometryTest {
|
||||
|
||||
|
||||
75
demo/playground/build.gradle.kts
Normal file
75
demo/playground/build.gradle.kts
Normal file
@@ -0,0 +1,75 @@
|
||||
plugins {
|
||||
kotlin("multiplatform")
|
||||
}
|
||||
|
||||
repositories{
|
||||
jcenter()
|
||||
maven("https://kotlin.bintray.com/kotlinx")
|
||||
maven("https://dl.bintray.com/kotlin/kotlin-eap")
|
||||
maven("https://dl.bintray.com/mipt-npm/dataforge")
|
||||
maven("https://dl.bintray.com/mipt-npm/kscience")
|
||||
maven("https://dl.bintray.com/mipt-npm/dev")
|
||||
}
|
||||
|
||||
kotlin {
|
||||
|
||||
js(IR) {
|
||||
useCommonJs()
|
||||
browser {
|
||||
webpackTask {
|
||||
this.outputFileName = "js/visionforge-playground.js"
|
||||
}
|
||||
commonWebpackConfig {
|
||||
sourceMaps = false
|
||||
cssSupport.enabled = false
|
||||
}
|
||||
}
|
||||
binaries.executable()
|
||||
}
|
||||
|
||||
jvm{
|
||||
compilations.all {
|
||||
kotlinOptions.jvmTarget = "11"
|
||||
}
|
||||
testRuns["test"].executionTask.configure {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
val jsBrowserDistribution by tasks.getting
|
||||
|
||||
tasks.getByName<ProcessResources>("jvmProcessResources") {
|
||||
dependsOn(jsBrowserDistribution)
|
||||
afterEvaluate {
|
||||
from(jsBrowserDistribution)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sourceSets {
|
||||
val commonMain by getting {
|
||||
dependencies {
|
||||
api(project(":visionforge-solid"))
|
||||
api(project(":visionforge-gdml"))
|
||||
api(project(":visionforge-plotly"))
|
||||
}
|
||||
}
|
||||
|
||||
val jsMain by getting{
|
||||
dependencies {
|
||||
implementation(project(":ui:ring"))
|
||||
api(project(":visionforge-threejs"))
|
||||
}
|
||||
}
|
||||
|
||||
val jvmMain by getting{
|
||||
dependencies {
|
||||
api(project(":visionforge-server"))
|
||||
api("ch.qos.logback:logback-classic:1.2.3")
|
||||
implementation("com.github.Ricky12Awesome:json-schema-serialization:0.6.6")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
10
demo/playground/src/jsMain/kotlin/playgroundMain.kt
Normal file
10
demo/playground/src/jsMain/kotlin/playgroundMain.kt
Normal file
@@ -0,0 +1,10 @@
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.visionforge.plotly.PlotlyPlugin
|
||||
import space.kscience.visionforge.ring.ThreeWithControlsPlugin
|
||||
import space.kscience.visionforge.runVisionClient
|
||||
|
||||
@DFExperimental
|
||||
fun main() = runVisionClient {
|
||||
plugin(PlotlyPlugin)
|
||||
plugin(ThreeWithControlsPlugin)
|
||||
}
|
||||
244
demo/playground/src/jvmMain/kotlin/gdmCurve.kt
Normal file
244
demo/playground/src/jvmMain/kotlin/gdmCurve.kt
Normal file
@@ -0,0 +1,244 @@
|
||||
package space.kscience.visionforge.examples
|
||||
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.gdml.*
|
||||
import space.kscience.visionforge.gdml.toVision
|
||||
import space.kscience.visionforge.html.ResourceLocation
|
||||
import space.kscience.visionforge.solid.Solids
|
||||
import space.kscience.visionforge.solid.color
|
||||
import space.kscience.visionforge.solid.invoke
|
||||
import space.kscience.visionforge.visible
|
||||
import java.nio.file.Path
|
||||
|
||||
fun main() {
|
||||
val context = Context {
|
||||
plugin(Solids)
|
||||
}
|
||||
|
||||
context.makeVisionFile(Path.of("curves.html"), resourceLocation = ResourceLocation.EMBED) {
|
||||
vision("canvas") {
|
||||
Gdml {
|
||||
// geometry variables
|
||||
val worldSize = 500
|
||||
// chamber
|
||||
val chamberHeight = 30 // length of the chamber
|
||||
val chamberDiameter = 102 // inner diameter of the copper chamber
|
||||
val chamberOuterSquareSide = 134 // chamber has a square footprint
|
||||
val chamberBackplateThickness = 15 // thickness of the backplate of the chamber
|
||||
// teflon disk
|
||||
val cathodeTeflonDiskHoleRadius = 15
|
||||
val cathodeTeflonDiskThickness = 5
|
||||
val cathodeCopperSupportOuterRadius = 45
|
||||
val cathodeCopperSupportInnerRadius = 8.5
|
||||
val cathodeCopperSupportThickness = 1
|
||||
// mylar cathode
|
||||
val mylarCathodeThickness = 0.004
|
||||
// patern
|
||||
val cathodePatternLineWidth = 0.3
|
||||
val cathodePatternDiskRadius = 4.25
|
||||
// readout
|
||||
val chamberTeflonWallThickness = 1
|
||||
val readoutKaptonThickness = 0.5
|
||||
val readoutCopperThickness = 0.2
|
||||
val readoutPlaneSide = 60
|
||||
|
||||
structure {
|
||||
val worldMaterial = materials.composite("G4_AIR")
|
||||
val worldBox = solids.box(worldSize, worldSize, worldSize, name = "world")
|
||||
|
||||
val shieldingMaterial = materials.composite("G4_Pb")
|
||||
val scintillatorMaterial = materials.composite("BC408")
|
||||
val captureMaterial = materials.composite("G4_Cd")
|
||||
|
||||
// chamber
|
||||
val copperMaterial = materials.composite("G4_Cu")
|
||||
val chamberSolidBase = solids.box(chamberOuterSquareSide, chamberOuterSquareSide, chamberHeight)
|
||||
val chamberSolidHole = solids.tube(chamberDiameter / 2, chamberHeight)
|
||||
val chamberSolid = solids.subtraction(chamberSolidBase, chamberSolidHole)
|
||||
val chamberBodyVolume = volume(copperMaterial, chamberSolid)
|
||||
val chamberBackplateSolid =
|
||||
solids.box(chamberOuterSquareSide, chamberOuterSquareSide, chamberBackplateThickness)
|
||||
val chamberBackplateVolume = volume(copperMaterial, chamberBackplateSolid)
|
||||
// chamber teflon walls
|
||||
val teflonMaterial = materials.composite("G4_TEFLON")
|
||||
val chamberTeflonWallSolid = solids.tube(chamberDiameter / 2, chamberHeight) {
|
||||
rmin = chamberDiameter / 2.0 - chamberTeflonWallThickness
|
||||
}
|
||||
val chamberTeflonWallVolume = volume(teflonMaterial, chamberTeflonWallSolid)
|
||||
// cathode
|
||||
val cathodeCopperDiskMaterial = materials.composite("G4_Cu")
|
||||
val cathodeWindowMaterial = materials.composite("G4_MYLAR")
|
||||
|
||||
val cathodeTeflonDiskSolidBase =
|
||||
solids.tube(chamberOuterSquareSide / 2, cathodeTeflonDiskThickness) {
|
||||
rmin = cathodeTeflonDiskHoleRadius
|
||||
}
|
||||
val cathodeCopperDiskSolid =
|
||||
solids.tube(cathodeCopperSupportOuterRadius, cathodeCopperSupportThickness) {
|
||||
rmin = cathodeCopperSupportInnerRadius
|
||||
}
|
||||
|
||||
val cathodeTeflonDiskSolid = solids.subtraction(cathodeTeflonDiskSolidBase, cathodeCopperDiskSolid)
|
||||
val cathodeTeflonDiskVolume = volume(teflonMaterial, cathodeTeflonDiskSolid)
|
||||
|
||||
val cathodeWindowSolid = solids.tube(cathodeTeflonDiskHoleRadius, mylarCathodeThickness)
|
||||
val cathodeWindowVolume = volume(cathodeWindowMaterial, cathodeWindowSolid)
|
||||
|
||||
val cathodeFillingMaterial = materials.composite("G4_Galactic")
|
||||
val cathodeFillingSolidBase = solids.tube(cathodeTeflonDiskHoleRadius, cathodeTeflonDiskThickness)
|
||||
|
||||
val cathodeFillingSolid = solids.subtraction(cathodeFillingSolidBase, cathodeCopperDiskSolid) {
|
||||
position(z = chamberHeight / 2 - mylarCathodeThickness / 2)
|
||||
}
|
||||
val cathodeFillingVolume = volume(cathodeFillingMaterial, cathodeFillingSolid)
|
||||
|
||||
// pattern
|
||||
val cathodePatternLineAux = solids.box(
|
||||
cathodePatternLineWidth,
|
||||
cathodeCopperSupportInnerRadius * 2,
|
||||
cathodeCopperSupportThickness
|
||||
)
|
||||
val cathodePatternCentralHole = solids.tube(
|
||||
cathodePatternDiskRadius - 0 * cathodePatternLineWidth,
|
||||
cathodeCopperSupportThickness * 1.1
|
||||
)
|
||||
val cathodePatternLine = solids.subtraction(cathodePatternLineAux, cathodePatternCentralHole)
|
||||
|
||||
val cathodePatternDisk = solids.tube(
|
||||
cathodePatternDiskRadius,
|
||||
cathodeCopperSupportThickness
|
||||
) { rmin = cathodePatternDiskRadius - cathodePatternLineWidth }
|
||||
|
||||
|
||||
val cathodeCopperDiskSolidAux0 =
|
||||
solids.union(cathodeCopperDiskSolid, cathodePatternLine) {
|
||||
rotation(x = 0, y = 0, z = 0)
|
||||
}
|
||||
val cathodeCopperDiskSolidAux1 =
|
||||
solids.union(cathodeCopperDiskSolidAux0, cathodePatternLine) {
|
||||
rotation = GdmlRotation(
|
||||
"cathodePatternRotation1", x = 0, y = 0, z = 45
|
||||
)
|
||||
}
|
||||
val cathodeCopperDiskSolidAux2 =
|
||||
solids.union(cathodeCopperDiskSolidAux1, cathodePatternLine) {
|
||||
rotation = GdmlRotation(
|
||||
"cathodePatternRotation2", x = 0, y = 0, z = 90
|
||||
)
|
||||
}
|
||||
val cathodeCopperDiskSolidAux3 =
|
||||
solids.union(cathodeCopperDiskSolidAux2, cathodePatternLine) {
|
||||
rotation = GdmlRotation(
|
||||
"cathodePatternRotation3", x = 0, y = 0, z = 135
|
||||
)
|
||||
}
|
||||
|
||||
val cathodeCopperDiskFinal =
|
||||
solids.union(cathodeCopperDiskSolidAux3, cathodePatternDisk)
|
||||
|
||||
|
||||
val cathodeCopperDiskVolume =
|
||||
volume(cathodeCopperDiskMaterial, cathodeCopperDiskFinal)
|
||||
|
||||
val gasSolidOriginal = solids.tube(
|
||||
chamberDiameter / 2 - chamberTeflonWallThickness,
|
||||
chamberHeight
|
||||
)
|
||||
|
||||
val kaptonReadoutMaterial = materials.composite("G4_KAPTON")
|
||||
val kaptonReadoutSolid = solids.box(
|
||||
chamberOuterSquareSide,
|
||||
chamberOuterSquareSide,
|
||||
readoutKaptonThickness)
|
||||
val kaptonReadoutVolume = volume( kaptonReadoutMaterial, kaptonReadoutSolid)
|
||||
|
||||
val copperReadoutSolid =
|
||||
solids.box(readoutPlaneSide, readoutPlaneSide, readoutCopperThickness)
|
||||
val copperReadoutVolume = volume(copperMaterial, copperReadoutSolid)
|
||||
|
||||
val gasSolidAux =
|
||||
solids.subtraction(gasSolidOriginal, copperReadoutSolid) {
|
||||
position(z = -chamberHeight / 2 + readoutCopperThickness / 2)
|
||||
}
|
||||
|
||||
val gasMaterial = materials.composite("G4_Ar")
|
||||
val gasSolid =
|
||||
solids.subtraction( gasSolidAux, cathodeWindowSolid) {
|
||||
position(z = chamberHeight / 2 - mylarCathodeThickness / 2)
|
||||
rotation(z = 45)
|
||||
}
|
||||
val gasVolume = volume(gasMaterial, gasSolid)
|
||||
|
||||
// world setup
|
||||
world = volume(worldMaterial, worldBox) {
|
||||
physVolume(gasVolume) {
|
||||
name = "gas"
|
||||
}
|
||||
physVolume(kaptonReadoutVolume) {
|
||||
name = "kaptonReadout"
|
||||
position {
|
||||
z = -chamberHeight / 2 - readoutKaptonThickness / 2
|
||||
}
|
||||
}
|
||||
physVolume(copperReadoutVolume) {
|
||||
name = "copperReadout"
|
||||
position {
|
||||
z = -chamberHeight / 2 + readoutCopperThickness / 2
|
||||
}
|
||||
rotation { z = 45 }
|
||||
}
|
||||
physVolume(chamberBodyVolume) {
|
||||
name = "chamberBody"
|
||||
}
|
||||
physVolume(chamberBackplateVolume) {
|
||||
name = "chamberBackplate"
|
||||
position {
|
||||
z = -chamberHeight / 2 - readoutKaptonThickness - chamberBackplateThickness / 2
|
||||
}
|
||||
}
|
||||
physVolume(chamberTeflonWallVolume) {
|
||||
name = "chamberTeflonWall"
|
||||
}
|
||||
physVolume(cathodeTeflonDiskVolume) {
|
||||
name = "cathodeTeflonDisk"
|
||||
position {
|
||||
z = chamberHeight / 2 + cathodeTeflonDiskThickness / 2
|
||||
}
|
||||
}
|
||||
physVolume(cathodeCopperDiskVolume) {
|
||||
name = "cathodeCopperDisk"
|
||||
position {
|
||||
z = chamberHeight / 2 + cathodeCopperSupportThickness / 2
|
||||
}
|
||||
}
|
||||
physVolume(cathodeWindowVolume) {
|
||||
name = "cathodeWindow"
|
||||
position {
|
||||
z = chamberHeight / 2 - mylarCathodeThickness / 2
|
||||
}
|
||||
}
|
||||
physVolume(cathodeFillingVolume) {
|
||||
name = "cathodeFilling"
|
||||
position {
|
||||
z = chamberHeight / 2 + cathodeTeflonDiskThickness / 2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}.toVision {
|
||||
configure { parent, solid, material ->
|
||||
//disable visibility for the world box
|
||||
if(solid.name == "world"){
|
||||
visible = false
|
||||
}
|
||||
if(solid.name.startsWith("gas")){
|
||||
color("green")
|
||||
} else {
|
||||
//make all solids semi-transparent
|
||||
transparent()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
19
demo/playground/src/jvmMain/kotlin/gdmlCubes.kt
Normal file
19
demo/playground/src/jvmMain/kotlin/gdmlCubes.kt
Normal file
@@ -0,0 +1,19 @@
|
||||
package space.kscience.visionforge.examples
|
||||
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.gdml.GdmlShowCase
|
||||
import space.kscience.visionforge.gdml.toVision
|
||||
import space.kscience.visionforge.html.ResourceLocation
|
||||
import space.kscience.visionforge.solid.Solids
|
||||
|
||||
fun main() {
|
||||
val context = Context {
|
||||
plugin(Solids)
|
||||
}
|
||||
|
||||
context.makeVisionFile(resourceLocation = ResourceLocation.SYSTEM){
|
||||
vision("canvas") {
|
||||
GdmlShowCase.cubes().toVision()
|
||||
}
|
||||
}
|
||||
}
|
||||
16
demo/playground/src/jvmMain/kotlin/gdmlIaxo.kt
Normal file
16
demo/playground/src/jvmMain/kotlin/gdmlIaxo.kt
Normal file
@@ -0,0 +1,16 @@
|
||||
package space.kscience.visionforge.examples
|
||||
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.gdml.GdmlShowCase
|
||||
import space.kscience.visionforge.gdml.toVision
|
||||
import space.kscience.visionforge.solid.Solids
|
||||
|
||||
fun main() {
|
||||
val context = Context {
|
||||
plugin(Solids)
|
||||
}
|
||||
|
||||
context.makeVisionFile {
|
||||
vision("canvas") { GdmlShowCase.babyIaxo().toVision() }
|
||||
}
|
||||
}
|
||||
21
demo/playground/src/jvmMain/kotlin/generateSchema.kt
Normal file
21
demo/playground/src/jvmMain/kotlin/generateSchema.kt
Normal file
@@ -0,0 +1,21 @@
|
||||
package space.kscience.visionforge.examples
|
||||
|
||||
import com.github.ricky12awesome.jss.encodeToSchema
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.json.Json
|
||||
import space.kscience.visionforge.solid.SolidGroup
|
||||
import space.kscience.visionforge.solid.Solids
|
||||
|
||||
@ExperimentalSerializationApi
|
||||
fun main() {
|
||||
val schema = Json {
|
||||
serializersModule = Solids.serializersModuleForSolids
|
||||
prettyPrintIndent = " "
|
||||
prettyPrint = true
|
||||
ignoreUnknownKeys = true
|
||||
isLenient = true
|
||||
coerceInputValues = true
|
||||
encodeDefaults = true
|
||||
}.encodeToSchema(SolidGroup.serializer(), generateDefinitions = false)
|
||||
println(schema)
|
||||
}
|
||||
23
demo/playground/src/jvmMain/kotlin/plotlyVision.kt
Normal file
23
demo/playground/src/jvmMain/kotlin/plotlyVision.kt
Normal file
@@ -0,0 +1,23 @@
|
||||
package space.kscience.visionforge.examples
|
||||
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.plotly.scatter
|
||||
import space.kscience.visionforge.html.ResourceLocation
|
||||
import space.kscience.visionforge.plotly.PlotlyPlugin
|
||||
import space.kscience.visionforge.plotly.plotly
|
||||
|
||||
fun main() {
|
||||
val context = Context {
|
||||
plugin(PlotlyPlugin)
|
||||
}
|
||||
context.makeVisionFile(resourceLocation = ResourceLocation.SYSTEM){
|
||||
vision {
|
||||
plotly {
|
||||
scatter {
|
||||
x(1, 2, 3)
|
||||
y(5, 8, 7)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
41
demo/playground/src/jvmMain/kotlin/randomSpheres.kt
Normal file
41
demo/playground/src/jvmMain/kotlin/randomSpheres.kt
Normal file
@@ -0,0 +1,41 @@
|
||||
package space.kscience.visionforge.examples
|
||||
|
||||
import kotlinx.html.div
|
||||
import kotlinx.html.h1
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.visionforge.html.ResourceLocation
|
||||
import space.kscience.visionforge.solid.*
|
||||
import java.nio.file.Paths
|
||||
import kotlin.random.Random
|
||||
|
||||
fun main() {
|
||||
val context = Context {
|
||||
plugin(Solids)
|
||||
}
|
||||
|
||||
val random = Random(112233)
|
||||
|
||||
context.makeVisionFile(
|
||||
Paths.get("randomSpheres.html"),
|
||||
resourceLocation = ResourceLocation.EMBED
|
||||
) {
|
||||
h1 { +"Happy new year!" }
|
||||
div {
|
||||
vision {
|
||||
solid {
|
||||
repeat(100) {
|
||||
sphere(5, name = "sphere[$it]") {
|
||||
x = random.nextDouble(-300.0, 300.0)
|
||||
y = random.nextDouble(-300.0, 300.0)
|
||||
z = random.nextDouble(-300.0, 300.0)
|
||||
material {
|
||||
color(random.nextInt())
|
||||
}
|
||||
detail = 16
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
41
demo/playground/src/jvmMain/kotlin/serverExtensions.kt
Normal file
41
demo/playground/src/jvmMain/kotlin/serverExtensions.kt
Normal file
@@ -0,0 +1,41 @@
|
||||
package space.kscience.visionforge.examples
|
||||
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.visionforge.html.ResourceLocation
|
||||
import space.kscience.visionforge.html.VisionTagConsumer
|
||||
import space.kscience.visionforge.html.page
|
||||
import space.kscience.visionforge.html.scriptHeader
|
||||
import space.kscience.visionforge.makeFile
|
||||
import space.kscience.visionforge.three.server.VisionServer
|
||||
import space.kscience.visionforge.three.server.useScript
|
||||
import java.awt.Desktop
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
public fun VisionServer.usePlayground(): Unit {
|
||||
useScript("js/visionforge-playground.js")
|
||||
}
|
||||
|
||||
@OptIn(DFExperimental::class)
|
||||
public fun Context.makeVisionFile(
|
||||
path: Path? = null,
|
||||
title: String = "VisionForge page",
|
||||
resourceLocation: ResourceLocation = ResourceLocation.SYSTEM,
|
||||
show: Boolean = true,
|
||||
content: VisionTagConsumer<*>.() -> Unit
|
||||
): Unit {
|
||||
val actualPath = page(title, content = content).makeFile(path) { actualPath ->
|
||||
mapOf("threeJs" to scriptHeader("js/visionforge-playground.js", resourceLocation, actualPath))
|
||||
}
|
||||
if (show) Desktop.getDesktop().browse(actualPath.toFile().toURI())
|
||||
}
|
||||
|
||||
//@DFExperimental
|
||||
//public fun Context.makeVisionFile(
|
||||
// vision: Vision,
|
||||
// path: Path? = null,
|
||||
// title: String = "VisionForge page",
|
||||
// resourceLocation: ResourceLocation = ResourceLocation.SYSTEM,
|
||||
// show: Boolean = true,
|
||||
//): Unit = makeVisionFile({ vision(vision) }, path, title, resourceLocation, show)
|
||||
21
demo/playground/src/jvmMain/kotlin/simpleCube.kt
Normal file
21
demo/playground/src/jvmMain/kotlin/simpleCube.kt
Normal file
@@ -0,0 +1,21 @@
|
||||
package space.kscience.visionforge.examples
|
||||
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.visionforge.html.ResourceLocation
|
||||
import space.kscience.visionforge.solid.Solids
|
||||
import space.kscience.visionforge.solid.box
|
||||
import space.kscience.visionforge.solid.solid
|
||||
|
||||
fun main() {
|
||||
val context = Context {
|
||||
plugin(Solids)
|
||||
}
|
||||
|
||||
context.makeVisionFile(resourceLocation = ResourceLocation.SYSTEM){
|
||||
vision("canvas") {
|
||||
solid {
|
||||
box(100, 100, 100)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1065
demo/playground/src/jvmMain/resources/gdml/babyIAXO.gdml
Normal file
1065
demo/playground/src/jvmMain/resources/gdml/babyIAXO.gdml
Normal file
File diff suppressed because it is too large
Load Diff
3
demo/playground/webpack.config.d/01.ring.js
vendored
Normal file
3
demo/playground/webpack.config.d/01.ring.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
const ringConfig = require('@jetbrains/ring-ui/webpack.config').config;
|
||||
|
||||
config.module.rules.push(...ringConfig.module.rules)
|
||||
57
demo/plotly-fx/build.gradle.kts
Normal file
57
demo/plotly-fx/build.gradle.kts
Normal file
@@ -0,0 +1,57 @@
|
||||
plugins {
|
||||
kotlin("multiplatform")
|
||||
application
|
||||
id("org.openjfx.javafxplugin") version "0.0.9"
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven("https://repo.kotlin.link")
|
||||
mavenLocal()
|
||||
jcenter()
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvm {
|
||||
withJava()
|
||||
}
|
||||
|
||||
js(IR) {
|
||||
browser()
|
||||
}
|
||||
|
||||
sourceSets{
|
||||
commonMain{
|
||||
dependencies {
|
||||
|
||||
}
|
||||
}
|
||||
val jvmMain by getting {
|
||||
dependencies {
|
||||
implementation("no.tornado:tornadofx:1.7.19")
|
||||
implementation(project(":visionforge-server"))
|
||||
implementation("ch.qos.logback:logback-classic:1.2.3")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
javafx {
|
||||
modules("javafx.web")
|
||||
version = "16"
|
||||
}
|
||||
|
||||
application {
|
||||
mainClassName = "space.kscience.plotly.fx.PlotlyFXAppKt"
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
//val compileKotlin: KotlinCompile by tasks
|
||||
//compileKotlin.kotlinOptions {
|
||||
// jvmTarget = "11"
|
||||
//}
|
||||
//val compileTestKotlin: KotlinCompile by tasks
|
||||
//compileTestKotlin.kotlinOptions {
|
||||
// jvmTarget = "11"
|
||||
//}
|
||||
23
demo/sat-demo/build.gradle.kts
Normal file
23
demo/sat-demo/build.gradle.kts
Normal file
@@ -0,0 +1,23 @@
|
||||
plugins {
|
||||
id("ru.mipt.npm.gradle.jvm")
|
||||
application
|
||||
}
|
||||
|
||||
|
||||
kscience {
|
||||
// useSerialization {
|
||||
// json()
|
||||
// }
|
||||
application()
|
||||
}
|
||||
|
||||
group = "ru.mipt.npm"
|
||||
|
||||
dependencies{
|
||||
implementation(project(":visionforge-threejs:visionforge-threejs-server"))
|
||||
implementation("ch.qos.logback:logback-classic:1.2.3")
|
||||
}
|
||||
|
||||
application {
|
||||
mainClass.set("ru.mipt.npm.sat.SatServerKt")
|
||||
}
|
||||
70
demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/geometry.kt
Normal file
70
demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/geometry.kt
Normal file
@@ -0,0 +1,70 @@
|
||||
package ru.mipt.npm.sat
|
||||
|
||||
import space.kscience.dataforge.meta.set
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.visionforge.solid.*
|
||||
import space.kscience.visionforge.style
|
||||
import space.kscience.visionforge.useStyle
|
||||
import kotlin.math.PI
|
||||
|
||||
@DFExperimental
|
||||
internal fun visionOfSatellite(
|
||||
layers: Int = 10,
|
||||
layerHeight: Number = 10,
|
||||
xSegments: Int = 3,
|
||||
ySegments: Int = xSegments,
|
||||
xSegmentSize: Number = 30,
|
||||
ySegmentSize: Number = xSegmentSize,
|
||||
fiberDiameter: Number = 1.0,
|
||||
): SolidGroup = SolidGroup {
|
||||
val transparent by style {
|
||||
this[SolidMaterial.MATERIAL_OPACITY_KEY] = 0.3
|
||||
}
|
||||
|
||||
val red by style {
|
||||
this[SolidMaterial.MATERIAL_COLOR_KEY] = "red"
|
||||
}
|
||||
|
||||
val blue by style {
|
||||
this[SolidMaterial.MATERIAL_COLOR_KEY] = "blue"
|
||||
}
|
||||
|
||||
val totalXSize = xSegments * xSegmentSize.toDouble()
|
||||
val totalYSize = ySegments * ySegmentSize.toDouble()
|
||||
for (layer in 1..layers) {
|
||||
group("layer[$layer]") {
|
||||
for (i in 1..xSegments) {
|
||||
for (j in 1..ySegments) {
|
||||
box(xSegmentSize, ySegmentSize, layerHeight, name = "segment[$i,$j]") {
|
||||
useStyle(transparent)
|
||||
z = (layer - 0.5) * layerHeight.toDouble()
|
||||
x = (i - 0.5) * xSegmentSize.toDouble()
|
||||
y = (j - 0.5) * ySegmentSize.toDouble()
|
||||
}
|
||||
}
|
||||
}
|
||||
group("fibers") {
|
||||
for (i in 1..xSegments) {
|
||||
cylinder(fiberDiameter, totalYSize) {
|
||||
useStyle(red)
|
||||
rotationX = PI / 2
|
||||
z = (layer - 1.0) * layerHeight.toDouble() + fiberDiameter.toDouble()
|
||||
x = (i - 0.5) * xSegmentSize.toDouble()
|
||||
y = totalYSize / 2
|
||||
}
|
||||
}
|
||||
|
||||
for (j in 1..ySegments) {
|
||||
cylinder(fiberDiameter, totalXSize) {
|
||||
useStyle(blue)
|
||||
rotationY = PI / 2
|
||||
z = (layer) * layerHeight.toDouble() - fiberDiameter.toDouble()
|
||||
y = (j - 0.5) * xSegmentSize.toDouble()
|
||||
x = totalXSize / 2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
61
demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/satServer.kt
Normal file
61
demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/satServer.kt
Normal file
@@ -0,0 +1,61 @@
|
||||
package ru.mipt.npm.sat
|
||||
|
||||
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.html.div
|
||||
import kotlinx.html.h1
|
||||
import space.kscience.dataforge.context.Global
|
||||
import space.kscience.dataforge.names.toName
|
||||
import space.kscience.visionforge.solid.*
|
||||
import space.kscience.visionforge.three.server.*
|
||||
import space.kscience.visionforge.visionManager
|
||||
import kotlin.random.Random
|
||||
|
||||
fun main() {
|
||||
val satContext = Global.buildContext ("sat") {
|
||||
plugin(Solids)
|
||||
}
|
||||
|
||||
//Create a geometry
|
||||
val sat = visionOfSatellite(ySegments = 3)
|
||||
|
||||
val server = satContext.visionManager.serve {
|
||||
//use client library
|
||||
useThreeJs()
|
||||
//use css
|
||||
useCss("css/styles.css")
|
||||
page {
|
||||
div("flex-column") {
|
||||
h1 { +"Satellite detector demo" }
|
||||
vision(sat)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
server.show()
|
||||
|
||||
GlobalScope.launch {
|
||||
while (isActive) {
|
||||
val randomLayer = Random.nextInt(1, 11)
|
||||
val randomI = Random.nextInt(1, 4)
|
||||
val randomJ = Random.nextInt(1, 4)
|
||||
val target = "layer[$randomLayer].segment[$randomI,$randomJ]".toName()
|
||||
val targetVision = sat[target] as Solid
|
||||
targetVision.color("red")
|
||||
delay(1000)
|
||||
targetVision.color.clear()
|
||||
delay(500)
|
||||
}
|
||||
}
|
||||
|
||||
println("Enter 'exit' to close server")
|
||||
while (readLine() != "exit") {
|
||||
//
|
||||
}
|
||||
|
||||
server.close()
|
||||
|
||||
}
|
||||
16
demo/sat-demo/src/main/resources/css/styles.css
Normal file
16
demo/sat-demo/src/main/resources/css/styles.css
Normal file
@@ -0,0 +1,16 @@
|
||||
body{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.flex-column{
|
||||
width: calc(100% - 15px);
|
||||
height: calc(100% - 15px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.visionforge-output{
|
||||
flex-grow: 1;
|
||||
}
|
||||
@@ -12,8 +12,8 @@ To see Java FX demo, run `demo/spatial-showcase/Tasks/application/run` Gradle ta
|
||||
|
||||
##### Example view for JS:
|
||||
|
||||

|
||||

|
||||
|
||||
##### Example view for Java FX:
|
||||
|
||||

|
||||

|
||||
44
demo/solid-showcase/build.gradle.kts
Normal file
44
demo/solid-showcase/build.gradle.kts
Normal file
@@ -0,0 +1,44 @@
|
||||
import ru.mipt.npm.gradle.DependencyConfiguration
|
||||
import ru.mipt.npm.gradle.FXModule
|
||||
|
||||
plugins {
|
||||
id("ru.mipt.npm.gradle.mpp")
|
||||
application
|
||||
}
|
||||
|
||||
kscience {
|
||||
useCoroutines()
|
||||
val fxVersion: String by rootProject.extra
|
||||
useFx(FXModule.CONTROLS, version = fxVersion, configuration = DependencyConfiguration.IMPLEMENTATION)
|
||||
application()
|
||||
}
|
||||
|
||||
kotlin {
|
||||
|
||||
jvm {
|
||||
withJava()
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
commonMain {
|
||||
dependencies {
|
||||
implementation(project(":visionforge-solid"))
|
||||
// implementation(project(":visionforge-gdml"))
|
||||
}
|
||||
}
|
||||
jvmMain {
|
||||
dependencies {
|
||||
implementation(project(":visionforge-fx"))
|
||||
}
|
||||
}
|
||||
jsMain {
|
||||
dependencies {
|
||||
implementation(project(":visionforge-threejs"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
application {
|
||||
mainClassName = "space.kscience.visionforge.solid.demo.FXDemoAppKt"
|
||||
}
|
||||
@@ -1,30 +1,35 @@
|
||||
package hep.dataforge.vision.solid.demo
|
||||
package space.kscience.visionforge.solid.demo
|
||||
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.invoke
|
||||
import hep.dataforge.names.toName
|
||||
import hep.dataforge.output.OutputManager
|
||||
import hep.dataforge.vision.Colors
|
||||
import hep.dataforge.vision.Vision
|
||||
import hep.dataforge.vision.solid.*
|
||||
import hep.dataforge.vision.solid.specifications.Canvas3DOptions
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.invoke
|
||||
import space.kscience.dataforge.names.toName
|
||||
import space.kscience.visionforge.Colors
|
||||
import space.kscience.visionforge.VisionLayout
|
||||
import space.kscience.visionforge.solid.*
|
||||
import space.kscience.visionforge.solid.specifications.Canvas3DOptions
|
||||
import space.kscience.visionforge.visible
|
||||
import kotlin.math.PI
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
import kotlin.random.Random
|
||||
|
||||
|
||||
fun OutputManager.demo(name: String, title: String = name, block: SolidGroup.() -> Unit) {
|
||||
fun VisionLayout<Solid>.demo(name: String, title: String = name, block: SolidGroup.() -> Unit) {
|
||||
val meta = Meta {
|
||||
"title" put title
|
||||
}
|
||||
val output = get(Vision::class, name.toName(), meta = meta)
|
||||
output.render (action = block)
|
||||
val vision = SolidGroup(block)
|
||||
render(name.toName(), vision)
|
||||
}
|
||||
|
||||
val canvasOptions = Canvas3DOptions {
|
||||
minSize = 500
|
||||
size {
|
||||
minSize = 400
|
||||
}
|
||||
axes {
|
||||
size = 500.0
|
||||
visible = true
|
||||
@@ -35,19 +40,27 @@ val canvasOptions = Canvas3DOptions {
|
||||
}
|
||||
}
|
||||
|
||||
fun OutputManager.showcase() {
|
||||
fun VisionLayout<Solid>.showcase() {
|
||||
demo("shapes", "Basic shapes") {
|
||||
box(100.0, 100.0, 100.0) {
|
||||
z = -110.0
|
||||
color("teal")
|
||||
}
|
||||
sphere(50.0) {
|
||||
x = 110
|
||||
detail = 16
|
||||
color("red")
|
||||
}
|
||||
tube(50, height = 10, innerRadius = 25, angle = PI) {
|
||||
y = 110
|
||||
detail = 16
|
||||
rotationX = PI / 4
|
||||
color("blue")
|
||||
}
|
||||
sphereLayer(50, 40, theta = PI / 2) {
|
||||
rotationX = -PI * 3 / 4
|
||||
z = 110
|
||||
color(Colors.pink)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,6 +68,7 @@ fun OutputManager.showcase() {
|
||||
val group = group {
|
||||
box(100, 100, 100) {
|
||||
z = 110.0
|
||||
opacity = 0.5
|
||||
}
|
||||
|
||||
box(100, 100, 100) {
|
||||
@@ -63,7 +77,7 @@ fun OutputManager.showcase() {
|
||||
//override color for this cube
|
||||
color(1530)
|
||||
|
||||
GlobalScope.launch(Dispatchers.Main) {
|
||||
launch(Dispatchers.Main) {
|
||||
while (isActive) {
|
||||
delay(500)
|
||||
visible = !(visible ?: false)
|
||||
@@ -72,7 +86,7 @@ fun OutputManager.showcase() {
|
||||
}
|
||||
}
|
||||
|
||||
GlobalScope.launch(Dispatchers.Main) {
|
||||
launch(Dispatchers.Main) {
|
||||
val random = Random(111)
|
||||
while (isActive) {
|
||||
delay(1000)
|
||||
@@ -132,42 +146,51 @@ fun OutputManager.showcase() {
|
||||
}
|
||||
}
|
||||
|
||||
fun OutputManager.showcaseCSG() {
|
||||
fun VisionLayout<Solid>.showcaseCSG() {
|
||||
demo("CSG.simple", "CSG operations") {
|
||||
composite(CompositeType.UNION) {
|
||||
box(100, 100, 100) {
|
||||
z = 50
|
||||
}
|
||||
sphere(50)
|
||||
material {
|
||||
color(Colors.lightgreen)
|
||||
opacity = 0.3f
|
||||
}
|
||||
}
|
||||
composite(CompositeType.INTERSECT) {
|
||||
y = 300
|
||||
box(100, 100, 100) {
|
||||
z = 50
|
||||
}
|
||||
sphere(50)
|
||||
color(Colors.red)
|
||||
sphere(50) {
|
||||
detail = 32
|
||||
}
|
||||
material {
|
||||
color(Colors.pink)
|
||||
}
|
||||
}
|
||||
composite(CompositeType.UNION) {
|
||||
box(100, 100, 100) {
|
||||
z = 50
|
||||
}
|
||||
sphere(50) {
|
||||
detail = 32
|
||||
}
|
||||
color("lightgreen")
|
||||
opacity = 0.7
|
||||
}
|
||||
composite(CompositeType.SUBTRACT) {
|
||||
y = -300
|
||||
box(100, 100, 100) {
|
||||
z = 50
|
||||
}
|
||||
sphere(50)
|
||||
color(Colors.blue)
|
||||
sphere(50) {
|
||||
detail = 32
|
||||
}
|
||||
color("teal")
|
||||
opacity = 0.7
|
||||
}
|
||||
}
|
||||
|
||||
demo("CSG.custom", "CSG with manually created object") {
|
||||
intersect {
|
||||
tube(60, 10) {
|
||||
detail = 64
|
||||
cylinder(60, 10) {
|
||||
detail = 32
|
||||
}
|
||||
box(100, 100, 100)
|
||||
color("red")
|
||||
opacity = 0.5
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package space.kscience.visionforge.solid.demo
|
||||
|
||||
import kotlinx.browser.document
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import space.kscience.visionforge.Application
|
||||
import space.kscience.visionforge.solid.x
|
||||
import space.kscience.visionforge.solid.y
|
||||
import space.kscience.visionforge.startApplication
|
||||
import kotlin.random.Random
|
||||
|
||||
private class ThreeDemoApp : Application {
|
||||
|
||||
override fun start(state: Map<String, Any>) {
|
||||
|
||||
val element = document.getElementById("demo") ?: error("Element with id 'demo' not found on page")
|
||||
|
||||
ThreeDemoGrid(element).run {
|
||||
showcase()
|
||||
showcaseCSG()
|
||||
demo("dynamicBox", "Dancing boxes") {
|
||||
val boxes = (-10..10).flatMap { i ->
|
||||
(-10..10).map { j ->
|
||||
varBox(10, 10, name = "cell_${i}_${j}") {
|
||||
x = i * 10
|
||||
y = j * 10
|
||||
value = 128
|
||||
}
|
||||
}
|
||||
}
|
||||
GlobalScope.launch {
|
||||
while (isActive) {
|
||||
delay(500)
|
||||
boxes.forEach { box ->
|
||||
box.value = (box.value + Random.nextInt(-15, 15)).coerceIn(1..255)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
startApplication(::ThreeDemoApp)
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package space.kscience.visionforge.solid.demo
|
||||
|
||||
import kotlinx.browser.document
|
||||
import kotlinx.dom.clear
|
||||
import kotlinx.html.dom.append
|
||||
import kotlinx.html.id
|
||||
import kotlinx.html.js.*
|
||||
import kotlinx.html.style
|
||||
import org.w3c.dom.Element
|
||||
import org.w3c.dom.HTMLDivElement
|
||||
import org.w3c.dom.HTMLElement
|
||||
import space.kscience.dataforge.context.Global
|
||||
import space.kscience.dataforge.context.fetch
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.get
|
||||
import space.kscience.dataforge.meta.string
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.visionforge.VisionLayout
|
||||
import space.kscience.visionforge.solid.Solid
|
||||
import space.kscience.visionforge.solid.three.ThreeCanvas
|
||||
import space.kscience.visionforge.solid.three.ThreePlugin
|
||||
|
||||
class ThreeDemoGrid(element: Element) : VisionLayout<Solid> {
|
||||
private lateinit var navigationElement: HTMLElement
|
||||
private lateinit var contentElement: HTMLDivElement
|
||||
|
||||
private val outputs: MutableMap<Name, ThreeCanvas> = HashMap()
|
||||
|
||||
private val three = Global.fetch(ThreePlugin)
|
||||
|
||||
init {
|
||||
element.clear()
|
||||
element.append {
|
||||
nav("navbar navbar-expand-md navbar-dark fixed-top bg-dark") {
|
||||
a(classes = "navbar-brand") {
|
||||
href = "#"
|
||||
+"Demo grid"
|
||||
}
|
||||
div("navbar-collapse collapse") {
|
||||
id = "navbar"
|
||||
navigationElement = ul("nav navbar-nav")
|
||||
}
|
||||
}
|
||||
contentElement = div {
|
||||
id = "content"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun render(name: Name, vision: Solid, meta: Meta) {
|
||||
outputs.getOrPut(name) {
|
||||
navigationElement.append {
|
||||
li("nav-item") {
|
||||
a(classes = "nav-link") {
|
||||
href = "#$name"
|
||||
+name.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
contentElement.append {
|
||||
div("container") {
|
||||
id = name.toString()
|
||||
hr()
|
||||
h2 { +(meta["title"].string ?: name.toString()) }
|
||||
hr()
|
||||
div {
|
||||
style = "height: 600px;"
|
||||
id = "output-$name"
|
||||
}
|
||||
}
|
||||
}
|
||||
val element = document.getElementById("output-$name") ?: error("Element not found")
|
||||
three.getOrCreateCanvas(element, canvasOptions)
|
||||
}.render(vision)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
package space.kscience.visionforge.solid.demo
|
||||
|
||||
import info.laht.threekt.core.Object3D
|
||||
import info.laht.threekt.geometries.BoxGeometry
|
||||
import info.laht.threekt.objects.Mesh
|
||||
import space.kscience.dataforge.meta.int
|
||||
import space.kscience.dataforge.meta.number
|
||||
import space.kscience.dataforge.names.asName
|
||||
import space.kscience.dataforge.names.startsWith
|
||||
import space.kscience.dataforge.values.asValue
|
||||
import space.kscience.visionforge.onPropertyChange
|
||||
import space.kscience.visionforge.set
|
||||
import space.kscience.visionforge.setProperty
|
||||
import space.kscience.visionforge.solid.SolidGroup
|
||||
import space.kscience.visionforge.solid.layer
|
||||
import space.kscience.visionforge.solid.three.*
|
||||
import kotlin.math.max
|
||||
|
||||
internal fun SolidGroup.varBox(
|
||||
xSize: Number,
|
||||
ySize: Number,
|
||||
name: String = "",
|
||||
action: VariableBox.() -> Unit = {},
|
||||
): VariableBox = VariableBox(xSize, ySize).apply(action).also { set(name, it) }
|
||||
|
||||
internal class VariableBox(val xSize: Number, val ySize: Number) : ThreeJsVision() {
|
||||
|
||||
override fun render(three: ThreePlugin): Object3D {
|
||||
val geometry = BoxGeometry(xSize, ySize, 1)
|
||||
|
||||
val material = ThreeMaterials.DEFAULT.clone()
|
||||
|
||||
val mesh = Mesh(geometry, material).apply {
|
||||
//updateMaterial(this@VariableBox)
|
||||
applyEdges(this@VariableBox)
|
||||
//applyWireFrame(this@VariableBox)
|
||||
|
||||
//set position for mesh
|
||||
updatePosition(this@VariableBox)
|
||||
|
||||
layers.enable(this@VariableBox.layer)
|
||||
children.forEach {
|
||||
it.layers.enable(this@VariableBox.layer)
|
||||
}
|
||||
}
|
||||
mesh.scale.z = getOwnProperty(VALUE).number?.toDouble() ?: 1.0
|
||||
|
||||
//add listener to object properties
|
||||
onPropertyChange(three.context) { name ->
|
||||
when {
|
||||
name == VALUE -> {
|
||||
val value = getOwnProperty(VALUE).int ?: 0
|
||||
val size = value.toFloat() / 255f * 20f
|
||||
mesh.scale.z = size.toDouble()
|
||||
mesh.position.z = size.toDouble() / 2
|
||||
|
||||
val b = max(0, 128 - value)
|
||||
val r = max(0, value - 128)
|
||||
val g = 255 - b - r
|
||||
material.color.setRGB(r.toFloat() / 256, g.toFloat() / 256, b.toFloat() / 256)
|
||||
mesh.updateMatrix()
|
||||
}
|
||||
name.startsWith(MeshThreeFactory.EDGES_KEY) -> mesh.applyEdges(this@VariableBox)
|
||||
else -> mesh.updateProperty(this@VariableBox, name)
|
||||
}
|
||||
}
|
||||
|
||||
return mesh
|
||||
}
|
||||
|
||||
var value: Int
|
||||
get() = getOwnProperty(VALUE).int ?: 0
|
||||
set(value) {
|
||||
setProperty(VALUE, value.asValue())
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val VALUE = "value".asName()
|
||||
//
|
||||
// private val X_SIZE_KEY = GEOMETRY_KEY + "xSize"
|
||||
// private val Y_SIZE_KEY = GEOMETRY_KEY + "ySize"
|
||||
// private val Z_SIZE_KEY = GEOMETRY_KEY + "zSize"
|
||||
}
|
||||
}
|
||||
27
demo/solid-showcase/src/jsMain/resources/index.html
Normal file
27
demo/solid-showcase/src/jsMain/resources/index.html
Normal file
@@ -0,0 +1,27 @@
|
||||
<!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>
|
||||
<!-- CSS -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css"
|
||||
integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
|
||||
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
|
||||
<script type="text/javascript" src="solid-showcase.js"></script>
|
||||
</head>
|
||||
<body class="application">
|
||||
<div class="container" id="demo"></div>
|
||||
|
||||
<!-- jQuery and JS bundle w/ Popper.js -->
|
||||
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"
|
||||
integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj"
|
||||
crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.bundle.min.js"
|
||||
integrity="sha384-ho+j7jyWK8fNQe+A12Hb8AhRq26LrZ/JpcUGGOn+Y7RsweNrtN/tE3MoK7ZeZDyx"
|
||||
crossorigin="anonymous"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
4
demo/solid-showcase/src/jsMain/resources/styles.css
Normal file
4
demo/solid-showcase/src/jsMain/resources/styles.css
Normal file
@@ -0,0 +1,4 @@
|
||||
body{
|
||||
min-height: 75rem;
|
||||
padding-top: 4.5rem;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package space.kscience.visionforge.solid.demo
|
||||
|
||||
import javafx.stage.Stage
|
||||
import tornadofx.*
|
||||
|
||||
class FXDemoApp : App(FXDemoGrid::class) {
|
||||
|
||||
val view: FXDemoGrid by inject()
|
||||
|
||||
override fun start(stage: Stage) {
|
||||
super.start(stage)
|
||||
|
||||
stage.width = 600.0
|
||||
stage.height = 600.0
|
||||
|
||||
view.showcase()
|
||||
view.showcaseCSG()
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
launch<FXDemoApp>()
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package space.kscience.visionforge.solid.demo
|
||||
|
||||
import javafx.collections.FXCollections
|
||||
import javafx.scene.Parent
|
||||
import javafx.scene.control.Tab
|
||||
import space.kscience.dataforge.context.Global
|
||||
import space.kscience.dataforge.context.fetch
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.visionforge.VisionLayout
|
||||
import space.kscience.visionforge.solid.FX3DPlugin
|
||||
import space.kscience.visionforge.solid.FXCanvas3D
|
||||
import space.kscience.visionforge.solid.Solid
|
||||
import tornadofx.*
|
||||
|
||||
class FXDemoGrid : View(title = "DataForge-vis FX demo"), VisionLayout<Solid> {
|
||||
private val outputs = FXCollections.observableHashMap<Name, FXCanvas3D>()
|
||||
|
||||
override val root: Parent = borderpane {
|
||||
center = tabpane {
|
||||
tabs.bind(outputs) { key: Name, value: FXCanvas3D ->
|
||||
Tab(key.toString(), value.root)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val fx3d = Global.fetch(FX3DPlugin)
|
||||
|
||||
override fun render(name: Name, vision: Solid, meta: Meta) {
|
||||
outputs.getOrPut(name) { FXCanvas3D(fx3d, canvasOptions) }.render(vision)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
package hep.dataforge.vision.demo
|
||||
package space.kscience.visionforge.demo
|
||||
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.asConfig
|
||||
import hep.dataforge.meta.descriptors.NodeDescriptor
|
||||
import hep.dataforge.values.ValueType
|
||||
import hep.dataforge.vision.editor.ConfigEditor
|
||||
import hep.dataforge.vision.editor.FXMeta
|
||||
import hep.dataforge.vision.editor.MetaViewer
|
||||
import javafx.geometry.Orientation
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.asConfig
|
||||
import space.kscience.dataforge.meta.descriptors.NodeDescriptor
|
||||
import space.kscience.dataforge.values.ValueType
|
||||
import space.kscience.visionforge.editor.ConfigEditor
|
||||
import space.kscience.visionforge.editor.FXMeta
|
||||
import space.kscience.visionforge.editor.MetaViewer
|
||||
import tornadofx.*
|
||||
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
import scientifik.DependencyConfiguration
|
||||
import scientifik.FXModule
|
||||
import scientifik.useFx
|
||||
|
||||
plugins {
|
||||
id("scientifik.mpp")
|
||||
id("application")
|
||||
}
|
||||
|
||||
val fxVersion: String by rootProject.extra
|
||||
useFx(FXModule.CONTROLS, version = fxVersion, configuration = DependencyConfiguration.IMPLEMENTATION)
|
||||
|
||||
kotlin {
|
||||
|
||||
jvm {
|
||||
withJava()
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
commonMain {
|
||||
dependencies {
|
||||
api(project(":visionforge-solid"))
|
||||
api(project(":visionforge-gdml"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
application {
|
||||
mainClassName = "hep.dataforge.vision.solid.demo.FXDemoAppKt"
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
package hep.dataforge.vision.solid.demo
|
||||
|
||||
import hep.dataforge.js.Application
|
||||
import hep.dataforge.js.startApplication
|
||||
import kotlin.browser.document
|
||||
|
||||
private class ThreeDemoApp : Application {
|
||||
|
||||
override fun start(state: Map<String, Any>) {
|
||||
|
||||
val element = document.getElementById("canvas") ?: error("Element with id 'canvas' not found on page")
|
||||
|
||||
ThreeDemoGrid(element).run {
|
||||
showcase()
|
||||
showcaseCSG()
|
||||
// demo("dynamicBox", "Dancing boxes") {
|
||||
// val boxes = (-10..10).flatMap { i ->
|
||||
// (-10..10).map { j ->
|
||||
// varBox(10, 10, 0, name = "cell_${i}_${j}") {
|
||||
// x = i * 10
|
||||
// y = j * 10
|
||||
// value = 128
|
||||
// setProperty(EDGES_ENABLED_KEY, false)
|
||||
// setProperty(WIREFRAME_ENABLED_KEY, false)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// GlobalScope.launch {
|
||||
// while (isActive) {
|
||||
// delay(500)
|
||||
// boxes.forEach { box ->
|
||||
// box.value = (box.value + Random.nextInt(-15, 15)).coerceIn(0..255)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun main() {
|
||||
startApplication(::ThreeDemoApp)
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
package hep.dataforge.vision.solid.demo
|
||||
|
||||
import hep.dataforge.context.Global
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.get
|
||||
import hep.dataforge.meta.string
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.output.OutputManager
|
||||
import hep.dataforge.output.Renderer
|
||||
import hep.dataforge.vision.Vision
|
||||
import hep.dataforge.vision.solid.three.ThreeCanvas
|
||||
import hep.dataforge.vision.solid.three.ThreePlugin
|
||||
import hep.dataforge.vision.solid.three.output
|
||||
import kotlinx.html.dom.append
|
||||
import kotlinx.html.dom.create
|
||||
import kotlinx.html.h2
|
||||
import kotlinx.html.hr
|
||||
import kotlinx.html.id
|
||||
import kotlinx.html.js.div
|
||||
import kotlinx.html.span
|
||||
import org.w3c.dom.Element
|
||||
import kotlin.browser.document
|
||||
import kotlin.dom.clear
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
class ThreeDemoGrid(element: Element, meta: Meta = Meta.EMPTY) : OutputManager {
|
||||
|
||||
private val gridRoot = document.create.div("row")
|
||||
private val outputs: MutableMap<Name, ThreeCanvas> = HashMap()
|
||||
|
||||
private val three = Global.plugins.fetch(ThreePlugin)
|
||||
|
||||
init {
|
||||
element.clear()
|
||||
element.append(gridRoot)
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : Any> get(type: KClass<out T>, name: Name, stage: Name, meta: Meta): Renderer<T> {
|
||||
|
||||
return outputs.getOrPut(name) {
|
||||
if (type != Vision::class) error("Supports only DisplayObject")
|
||||
lateinit var output: ThreeCanvas
|
||||
//TODO calculate cell width here using jquery
|
||||
gridRoot.append {
|
||||
span("border") {
|
||||
div("col-6") {
|
||||
div { id = "output-$name" }.also {
|
||||
output = three.output(it, canvasOptions)
|
||||
//output.attach(it)
|
||||
}
|
||||
hr()
|
||||
h2 { +(meta["title"].string ?: name.toString()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
output
|
||||
} as Renderer<T>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
@file:UseSerializers(Point3DSerializer::class)
|
||||
|
||||
package hep.dataforge.vision.solid.demo
|
||||
|
||||
import hep.dataforge.meta.int
|
||||
import hep.dataforge.meta.number
|
||||
import hep.dataforge.meta.setItem
|
||||
import hep.dataforge.names.plus
|
||||
import hep.dataforge.names.startsWith
|
||||
import hep.dataforge.values.asValue
|
||||
import hep.dataforge.vision.getProperty
|
||||
import hep.dataforge.vision.set
|
||||
import hep.dataforge.vision.solid.*
|
||||
import hep.dataforge.vision.solid.Solid.Companion.GEOMETRY_KEY
|
||||
import hep.dataforge.vision.solid.demo.VariableBoxThreeFactory.Z_SIZE_KEY
|
||||
import hep.dataforge.vision.solid.three.*
|
||||
import hep.dataforge.vision.solid.three.ThreeMaterials.getMaterial
|
||||
import info.laht.threekt.core.BufferGeometry
|
||||
import info.laht.threekt.core.Object3D
|
||||
import info.laht.threekt.geometries.BoxBufferGeometry
|
||||
import info.laht.threekt.objects.Mesh
|
||||
import kotlinx.serialization.UseSerializers
|
||||
import kotlin.math.max
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
internal var Solid.variableZSize: Number
|
||||
get() = getProperty(Z_SIZE_KEY, false).number ?: 0f
|
||||
set(value) {
|
||||
setItem(Z_SIZE_KEY, value.asValue())
|
||||
}
|
||||
|
||||
internal var Solid.value: Int
|
||||
get() = getProperty("value", false).int ?: 0
|
||||
set(value) {
|
||||
setItem("value", value.asValue())
|
||||
val size = value.toFloat() / 255f * 20f
|
||||
scaleZ = size
|
||||
z = -size / 2
|
||||
|
||||
val b = max(0, 255 - value)
|
||||
val r = max(0, value - 255)
|
||||
val g = 255 - b - r
|
||||
color(r.toUByte(), g.toUByte(), b.toUByte())
|
||||
}
|
||||
|
||||
fun SolidGroup.varBox(
|
||||
xSize: Number,
|
||||
ySize: Number,
|
||||
zSize: Number,
|
||||
name: String = "",
|
||||
action: Solid.() -> Unit = {}
|
||||
) = CustomThreeVision(VariableBoxThreeFactory).apply {
|
||||
scaleX = xSize
|
||||
scaleY = ySize
|
||||
scaleZ = zSize
|
||||
}.apply(action).also { set(name, it) }
|
||||
|
||||
private object VariableBoxThreeFactory : ThreeFactory<Solid> {
|
||||
val X_SIZE_KEY = GEOMETRY_KEY + "xSize"
|
||||
val Y_SIZE_KEY = GEOMETRY_KEY + "ySize"
|
||||
val Z_SIZE_KEY = GEOMETRY_KEY + "zSize"
|
||||
|
||||
override val type: KClass<in Solid> get() = Solid::class
|
||||
|
||||
override fun invoke(obj: Solid): Object3D {
|
||||
val xSize = obj.getProperty(X_SIZE_KEY, false).number?.toDouble() ?: 1.0
|
||||
val ySize = obj.getProperty(Y_SIZE_KEY, false).number?.toDouble() ?: 1.0
|
||||
val zSize = obj.getProperty(Z_SIZE_KEY, false).number?.toDouble() ?: 1.0
|
||||
val geometry = BoxBufferGeometry(1, 1, 1)
|
||||
|
||||
//JS sometimes tries to pass Geometry as BufferGeometry
|
||||
@Suppress("USELESS_IS_CHECK") if (geometry !is BufferGeometry) error("BufferGeometry expected")
|
||||
|
||||
val mesh = Mesh(geometry, getMaterial(obj)).apply {
|
||||
applyEdges(obj)
|
||||
applyWireFrame(obj)
|
||||
|
||||
//set position for mesh
|
||||
updatePosition(obj)
|
||||
|
||||
layers.enable(obj.layer)
|
||||
children.forEach {
|
||||
it.layers.enable(obj.layer)
|
||||
}
|
||||
}
|
||||
|
||||
mesh.scale.set(xSize, ySize, zSize)
|
||||
|
||||
//add listener to object properties
|
||||
obj.onPropertyChange(this) { name ->
|
||||
when {
|
||||
// name.startsWith(GEOMETRY_KEY) -> {
|
||||
// val newXSize = obj.getProperty(X_SIZE_KEY, false).number?.toDouble() ?: 1.0
|
||||
// val newYSize = obj.getProperty(Y_SIZE_KEY, false).number?.toDouble() ?: 1.0
|
||||
// val newZSize = obj.getProperty(Z_SIZE_KEY, false).number?.toDouble() ?: 1.0
|
||||
// mesh.scale.set(newXSize, newYSize, newZSize)
|
||||
// mesh.updateMatrix()
|
||||
// }
|
||||
name.startsWith(MeshThreeFactory.WIREFRAME_KEY) -> mesh.applyWireFrame(obj)
|
||||
name.startsWith(MeshThreeFactory.EDGES_KEY) -> mesh.applyEdges(obj)
|
||||
else -> mesh.updateProperty(obj, name)
|
||||
}
|
||||
}
|
||||
return mesh
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
<!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.4.1/css/bootstrap.min.css"
|
||||
integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
|
||||
<script type="text/javascript" src="main.bundle.js"></script>
|
||||
</head>
|
||||
<body class="application">
|
||||
<div class="container">
|
||||
<h1>Demo grid</h1>
|
||||
</div>
|
||||
<div class="container" id="canvas"></div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,35 +0,0 @@
|
||||
package hep.dataforge.vision.solid.demo
|
||||
|
||||
import hep.dataforge.vision.gdml.gdml
|
||||
import javafx.stage.Stage
|
||||
import tornadofx.*
|
||||
import java.nio.file.NoSuchFileException
|
||||
import java.nio.file.Paths
|
||||
|
||||
class FXDemoApp : App(FXDemoGrid::class) {
|
||||
|
||||
val view: FXDemoGrid by inject()
|
||||
|
||||
override fun start(stage: Stage) {
|
||||
super.start(stage)
|
||||
|
||||
stage.width = 600.0
|
||||
stage.height = 600.0
|
||||
|
||||
view.showcase()
|
||||
try {
|
||||
view.demo("gdml", "gdml-cubes") {
|
||||
gdml(Paths.get("D:\\Work\\Projects\\gdml.kt\\gdml-source\\cubes.gdml"))
|
||||
//setProperty(Material3D.MATERIAL_WIREFRAME_KEY, true)
|
||||
}
|
||||
}
|
||||
catch (e: NoSuchFileException) {
|
||||
println("GDML demo: Please specify the correct file path e.g. " +
|
||||
"visionforge\\demo\\gdml\\src\\commonMain\\resources\\cubes.gdml")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
launch<FXDemoApp>()
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package hep.dataforge.vision.solid.demo
|
||||
|
||||
import hep.dataforge.context.Global
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.output.OutputManager
|
||||
import hep.dataforge.output.Renderer
|
||||
import hep.dataforge.vision.Vision
|
||||
import hep.dataforge.vision.solid.fx.FX3DPlugin
|
||||
import hep.dataforge.vision.solid.fx.FXCanvas3D
|
||||
import javafx.collections.FXCollections
|
||||
import javafx.scene.Parent
|
||||
import javafx.scene.control.Tab
|
||||
import tornadofx.*
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
class FXDemoGrid : View(title = "DataForge-vis FX demo"), OutputManager {
|
||||
private val outputs = FXCollections.observableHashMap<Name, FXCanvas3D>()
|
||||
|
||||
override val root: Parent = borderpane {
|
||||
center = tabpane {
|
||||
tabs.bind(outputs) { key: Name, value: FXCanvas3D ->
|
||||
Tab(key.toString(), value.root)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val fx3d = Global.plugins.fetch(FX3DPlugin)
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : Any> get(type: KClass<out T>, name: Name, stage: Name, meta: Meta): Renderer<T> {
|
||||
return outputs.getOrPut(name) {
|
||||
if (type != Vision::class) kotlin.error("Supports only DisplayObject")
|
||||
val output = FXCanvas3D(fx3d, canvasOptions)
|
||||
|
||||
output
|
||||
} as Renderer<T>
|
||||
}
|
||||
|
||||
}
|
||||
7
docs/appendix.md
Normal file
7
docs/appendix.md
Normal file
@@ -0,0 +1,7 @@
|
||||
## Appendix
|
||||
|
||||
### DataForge Name
|
||||
|
||||
### DataForge Meta
|
||||
|
||||
### DataForge Context
|
||||
19
docs/design.md
Normal file
19
docs/design.md
Normal file
@@ -0,0 +1,19 @@
|
||||
## Library design
|
||||
The central point of the library design is the `Vision` interface. The `Vision` stores an optional reference to its parent and is able to store a number of mutable or read-only properties. Each property is represented by its `Name`, and a `MetaItem` value-tree, both following DataForge library specification (discussed in the [Appendix](appendix.md)). The `Vision` objects are organized in a tree using `VisionGroup` as nodes. `VisionGroup` additionally to all `Vision` properties holds a `children` container that holds named references to its direct children `Vision`s. Thus, `Vision`s form a doubly linked tree (a parent stores references to all its children and children store a reference to the parent).
|
||||
|
||||
An important concept using in the VisionForge is the property layering mechanism. It means that if the property with a given name is not found in the `Vision` it is requested from, it could be requested from the parent `Vision`, form the style declaration, the prototype for the vision or any other place defined by the component author. For example, let's take a `color` attribute used in 3D visualization. When one draws a group of objects, he usually wants to make the color of all objects in the group to be defined by a single handle in the group common ancestor. So when the parent color changes, all children color must follow suite, but we also want to change children color individually without changing the parent. In this case two property layers are defined:
|
||||
|
||||
* own properties layer
|
||||
* parent properties layer
|
||||
|
||||
When the user or a part of inner program mechanics requests a property, it searched in `Vision` own properties. If it is defined there, it is returned, if not, the search continues to the parent properties and up the parentage chain. So if the parent color is defined and children colors are blank, then the parent color will be used. If one of children redefines its own color, then this color is used for this specific color, and the parent color used for all other children. The change of parent property automatically propagated to all children.
|
||||
|
||||
### Styling
|
||||
|
||||
The actual layering scheme is more complicated. All objects support styling. The styles are named `Meta` nodes that could be used to define groups of properties together and use in different `Vision`s. The styles are defined as named `Meta` nodes in a `VisionGroup` `@stylesheet` property (DataForge states that names starting with `@` are reserved for system properties, not visible by default to the user). The style or a list of styles could be applied to a `Vision` by setting the reserved `@style` property to the name of the style being used or by using `Vision::style` extension property. The style name is then resolved to the nearest ancestor that defines it. The key difference from the CSS styling in HTML is that the stylesheets could be added to any node, not only to the hierarchy root. It allows to decouple the whole subtrees together with styles from the main `Vision` graph, or create stand-alone branches. One must note, that the same style could be defined on different levels of the tree, only the nearest ancestor is resolved, meaning that one can override styles for a sub-tree.
|
||||
|
||||
### Intermediate representation
|
||||
|
||||
An important thing about VisionForge is that it does not strictly bound to a single format representation.
|
||||
|
||||
### Kotlin DSL for creating vision-graphs
|
||||
BIN
docs/diagrams/Dependency structure.vsdx
Normal file
BIN
docs/diagrams/Dependency structure.vsdx
Normal file
Binary file not shown.
5
docs/features.md
Normal file
5
docs/features.md
Normal file
@@ -0,0 +1,5 @@
|
||||
## Features
|
||||
|
||||
### Customization and plugins
|
||||
|
||||
### Full-stack development
|
||||
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user