v0.2.0-dev-22 #47
2
.gitignore
vendored
@ -5,6 +5,4 @@ out/
|
||||
.gradle
|
||||
build/
|
||||
|
||||
|
||||
!gradle-wrapper.jar
|
||||
gradle.properties
|
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
|
18
README.md
@ -3,11 +3,14 @@
|
||||
|
||||
![Gradle build](https://github.com/mipt-npm/visionforge/workflows/Gradle%20build/badge.svg)
|
||||
|
||||
[![Slack](https://img.shields.io/badge/slack-channel-green?logo=slack)](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)
|
||||
@ -32,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
|
||||
|
||||
@ -67,7 +73,7 @@ The `visionforge-core` module also includes configuration editors for JS (in `js
|
||||
|
||||
**Class diagram:**
|
||||
|
||||
![](doc/resources/class-diag-core.png)
|
||||
![](docs/images/class-diag-core.png)
|
||||
|
||||
|
||||
### visionforge-solid
|
||||
@ -76,7 +82,7 @@ Includes common classes and serializers for 3D visualization, as well as Three.j
|
||||
|
||||
**Class diagram:**
|
||||
|
||||
![](doc/resources/class-diag-solid.png)
|
||||
![](docs/images/class-diag-solid.png)
|
||||
|
||||
##### Prototypes
|
||||
|
||||
@ -85,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
|
||||
|
||||
@ -121,7 +127,7 @@ Some shapes will also periodically change their color and visibility.
|
||||
|
||||
**Example view:**
|
||||
|
||||
![](doc/resources/spatial-showcase.png)
|
||||
![](docs/images/spatial-showcase.png)
|
||||
|
||||
|
||||
### Full-Stack Application Example - Muon Monitor Visualization
|
||||
@ -133,7 +139,7 @@ A full-stack application example, showing the
|
||||
|
||||
**Example view:**
|
||||
|
||||
![](doc/resources/muon-monitor.png)
|
||||
![](docs/images/muon-monitor.png)
|
||||
|
||||
|
||||
### GDML Example
|
||||
@ -144,7 +150,7 @@ Visualization example for geometry defined as GDML file.
|
||||
|
||||
##### Example view:
|
||||
|
||||
![](doc/resources/gdml-demo.png)
|
||||
![](docs/images/gdml-demo.png)
|
||||
|
||||
|
||||
## Thanks and references
|
||||
|
@ -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")
|
||||
plugins.apply("maven-publish")
|
||||
}
|
||||
useSerialization()
|
||||
useFx(scientifik.FXModule.CONTROLS, version = fxVersion)
|
||||
}
|
||||
|
||||
ksciencePublish{
|
||||
github("visionforge")
|
||||
space()
|
||||
sonatype()
|
||||
}
|
||||
|
||||
apiValidation {
|
||||
validationDisabled = true
|
||||
ignoredPackages.add("info.laht.threekt")
|
||||
}
|
@ -12,4 +12,4 @@ drag-and-drop GDML file to the window to see visualization. For an example file,
|
||||
|
||||
##### Example view:
|
||||
|
||||
![](../../doc/resources/gdml-demo.png)
|
||||
![](../../docs/images/gdml-demo.png)
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
kscience {
|
||||
val fxVersion: String by rootProject.extra
|
||||
useFx(FXModule.CONTROLS, version = fxVersion, configuration = DependencyConfiguration.IMPLEMENTATION)
|
||||
application()
|
||||
}
|
||||
|
||||
kotlin {
|
||||
|
||||
jvm {
|
||||
withJava()
|
||||
}
|
||||
|
||||
js{
|
||||
useCommonJs()
|
||||
browser {
|
||||
commonWebpackConfig {
|
||||
cssSupport.enabled = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
commonMain {
|
||||
dependencies {
|
||||
@ -27,9 +31,15 @@ kotlin {
|
||||
implementation(project(":visionforge-gdml"))
|
||||
}
|
||||
}
|
||||
jvmMain {
|
||||
dependencies {
|
||||
implementation(project(":visionforge-fx"))
|
||||
}
|
||||
}
|
||||
jsMain {
|
||||
dependencies {
|
||||
implementation(project(":ui:bootstrap"))
|
||||
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)
|
||||
if(file!= null) {
|
||||
runAsync {
|
||||
visionManager.readFile(file) as Solid
|
||||
} ui {
|
||||
if (it != null) {
|
||||
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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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:
|
||||
|
||||
![](../../doc/resources/muon-monitor.png)
|
||||
![](../../docs/images/muon-monitor.png)
|
||||
|
||||
|
@ -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 {
|
||||
@JsExport
|
||||
val MMApp = functionalComponent<MMAppProps>("Muon monitor") { props ->
|
||||
var selected by useState { props.selected }
|
||||
|
||||
val onSelect: (Name?) -> Unit = {
|
||||
selected = it
|
||||
}
|
||||
|
||||
val mmOptions = useMemo {
|
||||
Canvas3DOptions {
|
||||
camera = Camera {
|
||||
distance = 2100.0
|
||||
latitude = PI / 6
|
||||
azimuth = PI + PI / 6
|
||||
}
|
||||
}
|
||||
|
||||
val MMApp = component<MMAppProps> { props ->
|
||||
var selected by state { props.selected }
|
||||
var canvas: ThreeCanvas? by state { null }
|
||||
|
||||
val select: (Name?) -> Unit = {
|
||||
selected = it
|
||||
}
|
||||
|
||||
val visual = props.model.root
|
||||
|
||||
div("row") {
|
||||
h1("mx-auto") {
|
||||
+"Muon monitor demo"
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
flexColumn {
|
||||
css {
|
||||
+"col-lg-3"
|
||||
+"order-3"
|
||||
padding(0.px)
|
||||
height = 100.vh
|
||||
}
|
||||
styledDiv {
|
||||
css {
|
||||
flex(0.0, 1.0, FlexBasis.zero)
|
||||
}
|
||||
div("col-lg-3") {
|
||||
div("row") {
|
||||
//settings
|
||||
canvas?.let {
|
||||
card("Canvas configuration") {
|
||||
canvasControls(it)
|
||||
}
|
||||
canvasControls(mmOptions, root)
|
||||
}
|
||||
|
||||
card("Events") {
|
||||
button {
|
||||
+"Next"
|
||||
@ -106,8 +132,10 @@ val MMApp = component<MMAppProps> { props ->
|
||||
}
|
||||
}
|
||||
}
|
||||
div("row") {
|
||||
div("container-fluid p-0") {
|
||||
styledDiv {
|
||||
css {
|
||||
padding(0.px)
|
||||
}
|
||||
nav {
|
||||
attrs {
|
||||
attributes["aria-label"] = "breadcrumb"
|
||||
@ -118,7 +146,7 @@ val MMApp = component<MMAppProps> { props ->
|
||||
+"World"
|
||||
attrs {
|
||||
onClickFunction = {
|
||||
selected = hep.dataforge.names.Name.EMPTY
|
||||
selected = Name.EMPTY
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -144,18 +172,20 @@ val MMApp = component<MMAppProps> { props ->
|
||||
}
|
||||
}
|
||||
}
|
||||
styledDiv {
|
||||
css {
|
||||
overflowY = Overflow.auto
|
||||
}
|
||||
div("row") {
|
||||
//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,48 +1,47 @@
|
||||
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.model = this@MMDemoApp.model
|
||||
this.connection = this@MMDemoApp.connection
|
||||
this.context = context
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
startApplication(::MMDemoApp)
|
||||
|
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
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
@ -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
@ -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
@ -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
@ -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
@ -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:
|
||||
|
||||
![](../../doc/resources/spatial-showcase.png)
|
||||
![](../../docs/images/spatial-showcase.png)
|
||||
|
||||
##### Example view for Java FX:
|
||||
|
||||
![](../../doc/resources/spatial-showcase-FX.png)
|
||||
![](../../docs/images/spatial-showcase-FX.png)
|
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
@ -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
@ -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
@ -0,0 +1,7 @@
|
||||
## Appendix
|
||||
|
||||
### DataForge Name
|
||||
|
||||
### DataForge Meta
|
||||
|
||||
### DataForge Context
|
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
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 |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 97 KiB |
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 87 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
77
docs/solids.md
Normal file
@ -0,0 +1,77 @@
|
||||
## Solids
|
||||
|
||||
The primary initial goal of creating the library was to provide a flexible API and rendering engine to draw solid geometric forms for so-called event display task in particle physics. Event display is used to provide visualization for particle hit events in both accelerator and non-accelerator experiments (see [add links here] for examples). The event display system must have a way to render basic 3D shapes like boxes, tubes and spheres and particle tracks. An additional requirement is to be able to change coloring and transparency of different elements as well as custom attributes specific to the problem.
|
||||
|
||||
The `Solid` class inherits the `Vision` and has three additional nullable real vector properties: `position`, `rotation` and `scale`. Those properties are placed on the top level of the class instead of `properties` tree for performance optimization purposes. The `position` and `rotation` properties reflect the position offset in absolute units and rotation in radians relative to the parent and have defaults of `0.0`. Relativity means that if the parent `Vision` (`SolidGroup`) has `position = (1.0, 0.0, 1.0)` and a child has `position = (0.0, 2.0, 0.0)`, then the real offset of rendered object will be `(1.0, 2.0, 1.0)`. The same goes for the rotation. By default, the rotation uses intrinsic Cordan (Tait–Bryan) angles with `XYZ` order, but order could be changed with an inherited property `rotation.order`. The `scale` property is not inherited and represents local `Solid` scaling factor with default value of `1.0`.
|
||||
|
||||
The `SolidGroup` represents the specific kind of `VisionGroup` that has properties of a `Solid` and has one additional special property: the prototype container.
|
||||
|
||||
### Solid prototypes
|
||||
|
||||
One of the important requirements for event display system is the ability to render a huge number of primitives (tens and hundreds of thousands). This is especially important for accelerator experiments with fine-grained calorimeter detectors ([ref here]). The most memory and CPU-consuming task to render such structures is computing the geometry o equal elements (like calorimeter cells). In order to optimize rendering and model representation for such cases, we introduced so-called prototypes. The idea is that multiple visions could have the same prototype and share the definition and actual rendered geometry. The prototypes are stored in a special container property called `prototypes` in a `SolidGroup` object. In order to access those prototypes there is a special `Solid` object called `SolidReference`. It inherits `Solid` (has properties container, position, rotation and scale), but does not define any geometry. Instead, it has a single `refName` property which stores a fully qualified DataForge `Name` (see [Appendix](appendix.md)) that designates the name of the prototype to be used. During rendering phase, the system searches for appropriate prototype in the nearest parent. If the prototype is not found, the search continues in the next parent in the chain. Like property resolution, the prototype resolution is fine-grained and local, meaning that a part of `Vision` graph could be self-contained together with prototypes.
|
||||
|
||||
An important point is that prototypes are resolved in a parent vision container, and the parent vision container for prototypes is the vision group. It means that prototype itself could reference a prototype, or even in corner cases reference itself (such cases should be checked for infinite loops). The prototype relation example is shown in [prototypes.uml](uml/prototypes.puml). Here `Prototype Ref` vision references the `Prototype` vision by first seeking up to `VirionGroup`, the resolving the prototype with appropriate name. In theory, vision groups inside prototypes groups could contain the prototypes of their own, but such constructs should be used with care.
|
||||
|
||||
### Solid definitions
|
||||
|
||||
Solid models are defined as kotlin classes that also serve as builders for DSL and serialization models. For example, here is the definition for a `Box` class:
|
||||
|
||||
```kotlin
|
||||
@Serializable
|
||||
@SerialName("solid.box")
|
||||
class Box(
|
||||
val xSize: Float,
|
||||
val ySize: Float,
|
||||
val zSize: Float
|
||||
) : SolidBase(), GeometrySolid
|
||||
```
|
||||
|
||||
In general, one does not define how specific solid is rendered, it is done in the rendering back-end. It means that in general, there should be a specific renderer defined for each type of solids defined in the model. Some solids have additional mechanism to avoid providing renderers for each primitive. Classes that inherit `GeometrySolid` interface provide a way to define geometry via polygons like this:
|
||||
|
||||
```kotlin
|
||||
override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) {
|
||||
val dx = xSize / 2
|
||||
val dy = ySize / 2
|
||||
val dz = zSize / 2
|
||||
val node1 = Point3D(-dx, -dy, -dz)
|
||||
val node2 = Point3D(dx, -dy, -dz)
|
||||
val node3 = Point3D(dx, dy, -dz)
|
||||
val node4 = Point3D(-dx, dy, -dz)
|
||||
val node5 = Point3D(-dx, -dy, dz)
|
||||
val node6 = Point3D(dx, -dy, dz)
|
||||
val node7 = Point3D(dx, dy, dz)
|
||||
val node8 = Point3D(-dx, dy, dz)
|
||||
geometryBuilder.face4(node1, node4, node3, node2)
|
||||
geometryBuilder.face4(node1, node2, node6, node5)
|
||||
geometryBuilder.face4(node2, node3, node7, node6)
|
||||
geometryBuilder.face4(node4, node8, node7, node3)
|
||||
geometryBuilder.face4(node1, node5, node8, node4)
|
||||
geometryBuilder.face4(node8, node5, node6, node7)
|
||||
}
|
||||
```
|
||||
|
||||
The `GeometryBuilder` instance is provided by the specific rendering engine, so the polygons are generated for target engine without intermediate steps. If the renderer provides its own implementation for a solid renderer, it takes precedence over generic `GeometrySolid` definition.
|
||||
|
||||
An important basic solid is the `Composite`. It is a primitive, that allows combine two (maybe more in future) primitives via [composite solid geometry or CSG](https://en.wikipedia.org/wiki/Constructive_solid_geometry) transformations (union, difference and subtraction). At this moment, the actual transformation is done on the renderer side.
|
||||
|
||||
### GDML bindings
|
||||
|
||||
VisionForge supports extensible geometry primitive list, but we've started with primitives needed to render BM@N ([ref]) GDML ([ref]) geometry. GDML files are read with [gdml.kt](https://github.com/mipt-npm/gdml.kt) package designed specifically for this purpose. The package uses [https://github.com/pdvrieze/xmlutil](https://github.com/pdvrieze/xmlutil) serialization plugin to parse GDML structure into the convenient object structure and has language definitions for all GDML primitives. The `visionforge-gdml` module contains a converter that transforms Gdml.kt structures into `Solid`.
|
||||
|
||||
The GDML file definition contains several sections:
|
||||
|
||||
* **defines** - Dimensions and numbers for geometries.
|
||||
* **materials** - Material definitions.
|
||||
* **solids** - Primitive definitions and their composition.
|
||||
* **structure** - Combinations of solids and groups used in geometry.
|
||||
* **setup** - Metadata and geometry entry point.
|
||||
|
||||
GDML materials are currently used only for Solid coloring. The data from material is stored in the `Vision` properties. Solids section and structures are transformed into top level group prototypes, which allows not only to create a compact representation, but also optimize rendering. The gdml structure is not always flexible. For example, it is not possible to add a solid directly into a group, but instead one needs to add a group with a single element. Problems like this could be automatically optimized by the convertor.
|
||||
|
||||
Not all GDML solids are currently supported by the converter, they could be added later on-demand. Current support covers all solids used in BM@N geometry.
|
||||
|
||||
### ThreeJs renderer
|
||||
|
||||
VisionForge is not tied to any single renderer. Right now the primary target is the Browser rendering with [Three.js library](https://threejs.org/). The Three.js supports different renderers including WebGL with hardware support and virtual reality.
|
||||
|
||||
The bindings for three-js was implemented in kotlin-js based on a [work by Lars Ivar Hatledal](https://github.com/markaren/three-kt-wrapper). The wrapper allows seamless integration with a lot of different library APIs including custom cameras and CSG.
|
45
docs/uml/Vision.puml
Normal file
@ -0,0 +1,45 @@
|
||||
@startuml
|
||||
'https://plantuml.com/class-diagram
|
||||
|
||||
interface Vision{
|
||||
val parent: Vision?
|
||||
fun getProperty(name): MetaItem?
|
||||
fun setProperty(name, value)
|
||||
}
|
||||
|
||||
class VisionBase{
|
||||
basic vision
|
||||
implementation
|
||||
}
|
||||
Vision <|-- VisionBase
|
||||
|
||||
interface VisionGroup{
|
||||
A group of Visions
|
||||
}
|
||||
Vision <|-- VisionGroup
|
||||
|
||||
interface Solid{
|
||||
The base for 3D geometry
|
||||
}
|
||||
|
||||
Vision <|-- Solid
|
||||
|
||||
class SolidGroup
|
||||
|
||||
Solid <|-- SolidGroup
|
||||
VisionGroup <|-- SolidGroup
|
||||
|
||||
class Composite
|
||||
Solid <|-- Composite
|
||||
VisionGroup <|-- Composite
|
||||
|
||||
class Box
|
||||
Solid <|-- Box
|
||||
|
||||
class Tube
|
||||
Solid <|-- Tube
|
||||
|
||||
class Sphere
|
||||
Solid <|-- Sphere
|
||||
|
||||
@enduml
|
14
docs/uml/prototypes.puml
Normal file
@ -0,0 +1,14 @@
|
||||
@startuml
|
||||
package "VisionGroup" {
|
||||
package "Prototypes"{
|
||||
[Prototype]
|
||||
package "Prototype group"{
|
||||
[Prototype Ref] --> [Prototype]
|
||||
}
|
||||
}
|
||||
|
||||
package "Child group"{
|
||||
[Ref] --> [Prototype Ref]
|
||||
}
|
||||
}
|
||||
@enduml
|
7
gradle.properties
Normal file
@ -0,0 +1,7 @@
|
||||
kotlin.code.style=official
|
||||
kotlin.mpp.enableGranularSourceSetsMetadata=true
|
||||
kotlin.mpp.stability.nowarn=true
|
||||
kotlin.native.enableDependencyPropagation=false
|
||||
|
||||
org.gradle.jvmargs=-XX:MaxMetaspaceSize=1G
|
||||
org.gradle.parallel=true
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
2
gradlew
vendored
@ -82,6 +82,7 @@ esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
@ -129,6 +130,7 @@ fi
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
|
22
gradlew.bat
vendored
@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
@ -54,7 +54,7 @@ goto fail
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
@ -64,28 +64,14 @@ echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
|