Large API update
This commit is contained in:
parent
0164534004
commit
8189012b70
@ -1,3 +1,5 @@
|
||||
[![JetBrains Research](https://jb.gg/badges/research.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub)
|
||||
|
||||
# DataForge plugins for visualisation
|
||||
|
||||
## Common visualisation objects
|
||||
|
@ -1,8 +1,10 @@
|
||||
import scientifik.useSerialization
|
||||
|
||||
val dataforgeVersion by extra("0.1.5-dev-4")
|
||||
|
||||
plugins {
|
||||
val kotlinVersion = "1.3.61"
|
||||
val toolsVersion = "0.2.7"
|
||||
val toolsVersion = "0.3.1"
|
||||
|
||||
kotlin("jvm") version kotlinVersion apply false
|
||||
id("kotlin-dce-js") version kotlinVersion apply false
|
||||
@ -27,6 +29,10 @@ allprojects {
|
||||
version = "0.1.0-dev"
|
||||
}
|
||||
|
||||
subprojects{
|
||||
useSerialization("0.13.0")
|
||||
}
|
||||
|
||||
val githubProject by extra("dataforge-vis")
|
||||
val bintrayRepo by extra("dataforge")
|
||||
|
||||
|
@ -5,10 +5,6 @@ plugins {
|
||||
id("org.openjfx.javafxplugin")
|
||||
}
|
||||
|
||||
scientifik{
|
||||
withSerialization()
|
||||
}
|
||||
|
||||
val dataforgeVersion: String by rootProject.extra
|
||||
//val kvisionVersion: String by rootProject.extra("2.0.0-M1")
|
||||
|
||||
|
@ -1,8 +1,10 @@
|
||||
package hep.dataforge.vis.common
|
||||
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.MetaItem
|
||||
import hep.dataforge.names.*
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.NameToken
|
||||
import hep.dataforge.names.asName
|
||||
import hep.dataforge.names.isEmpty
|
||||
import kotlinx.serialization.Transient
|
||||
|
||||
|
||||
@ -16,46 +18,7 @@ abstract class AbstractVisualGroup : AbstractVisualObject(), MutableVisualGroup
|
||||
/**
|
||||
* A map of top level named children
|
||||
*/
|
||||
abstract override val children: Map<NameToken, VisualObject> //get() = _children
|
||||
|
||||
/**
|
||||
* Styles, defined in this group. A style could be defined but not applied
|
||||
* TODO replace by custom object with get/set functionality
|
||||
*/
|
||||
protected abstract val styleSheet: MutableMap<Name, Meta>
|
||||
|
||||
override fun getStyle(name: Name): Meta? = styleSheet[name]
|
||||
|
||||
override fun addStyle(name: Name, meta: Meta, apply: Boolean) {
|
||||
fun VisualObject.applyStyle(name: Name, meta: Meta) {
|
||||
if (styles.contains(name)) {
|
||||
//full update
|
||||
//TODO do a fine grained update
|
||||
if (this is AbstractVisualObject) {
|
||||
styleChanged()
|
||||
} else {
|
||||
propertyChanged(EmptyName)
|
||||
}
|
||||
}
|
||||
if (this is VisualGroup) {
|
||||
this.children.forEach { (_, child) ->
|
||||
child.applyStyle(name, meta)
|
||||
}
|
||||
}
|
||||
}
|
||||
styleSheet[name] = meta
|
||||
if (apply) {
|
||||
applyStyle(name, meta)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// init {
|
||||
// //Do after deserialization
|
||||
// children.values.forEach {
|
||||
// it.parent = this
|
||||
// }
|
||||
// }
|
||||
abstract override val children: Map<NameToken, VisualObject>
|
||||
|
||||
override fun propertyChanged(name: Name, before: MetaItem<*>?, after: MetaItem<*>?) {
|
||||
super.propertyChanged(name, before, after)
|
||||
|
@ -1,9 +1,8 @@
|
||||
package hep.dataforge.vis.common
|
||||
|
||||
import hep.dataforge.meta.*
|
||||
import hep.dataforge.names.EmptyName
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.toName
|
||||
import hep.dataforge.names.asName
|
||||
import hep.dataforge.vis.common.VisualObject.Companion.STYLE_KEY
|
||||
import kotlinx.serialization.Transient
|
||||
|
||||
@ -17,15 +16,25 @@ abstract class AbstractVisualObject : VisualObject {
|
||||
@Transient
|
||||
override var parent: VisualObject? = null
|
||||
|
||||
abstract override var properties: Config?
|
||||
protected abstract var properties: Config?
|
||||
|
||||
override var styles: List<Name>
|
||||
get() = properties?.get(STYLE_KEY).stringList.map(String::toName)
|
||||
override var styles: List<String>
|
||||
get() = properties?.get(STYLE_KEY).stringList
|
||||
set(value) {
|
||||
setProperty(STYLE_KEY, value.map { it.toString() })
|
||||
styleChanged()
|
||||
//val allStyles = (field + value).distinct()
|
||||
setProperty(STYLE_KEY, value)
|
||||
updateStyles(value)
|
||||
}
|
||||
|
||||
protected fun updateStyles(names: List<String>) {
|
||||
names.mapNotNull { findStyle(it) }.asSequence()
|
||||
.flatMap { it.items.asSequence() }
|
||||
.distinctBy { it.key }
|
||||
.forEach {
|
||||
propertyChanged(it.key.asName(), null, it.value)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The config is initialized and assigned on-demand.
|
||||
* To avoid unnecessary allocations, one should access [properties] via [getProperty] instead.
|
||||
@ -67,14 +76,6 @@ abstract class AbstractVisualObject : VisualObject {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper to reset style cache
|
||||
*/
|
||||
protected fun styleChanged() {
|
||||
styleCache = null
|
||||
propertyChanged(EmptyName)
|
||||
}
|
||||
|
||||
override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? {
|
||||
return if (inherit) {
|
||||
properties?.get(name) ?: mergedStyles[name] ?: parent?.getProperty(name, inherit)
|
||||
@ -84,10 +85,10 @@ abstract class AbstractVisualObject : VisualObject {
|
||||
}
|
||||
}
|
||||
|
||||
fun VisualObject.findStyle(styleName: Name): Meta? {
|
||||
if (this is VisualGroup) {
|
||||
val style = getStyle(styleName)
|
||||
if (style != null) return style
|
||||
}
|
||||
return parent?.findStyle(styleName)
|
||||
}
|
||||
//fun VisualObject.findStyle(styleName: Name): Meta? {
|
||||
// if (this is VisualGroup) {
|
||||
// val style = resolveStyle(styleName)
|
||||
// if (style != null) return style
|
||||
// }
|
||||
// return parent?.findStyle(styleName)
|
||||
//}
|
@ -1,5 +1,10 @@
|
||||
package hep.dataforge.vis.common
|
||||
|
||||
import hep.dataforge.meta.*
|
||||
import hep.dataforge.values.ValueType
|
||||
import hep.dataforge.values.int
|
||||
import kotlin.math.max
|
||||
|
||||
/**
|
||||
* Taken from https://github.com/markaren/three.kt/blob/master/threejs-wrapper/src/main/kotlin/info/laht/threekt/math/ColorConstants.kt
|
||||
*/
|
||||
@ -175,9 +180,38 @@ object Colors {
|
||||
const val yellow = 0xFFFF00
|
||||
const val yellowgreen = 0x9ACD32
|
||||
|
||||
const val RED_KEY = "red"
|
||||
const val GREEN_KEY = "green"
|
||||
const val BLUE_KEY = "blue"
|
||||
|
||||
fun fromMeta(item: MetaItem<*>): String {
|
||||
return when (item) {
|
||||
is MetaItem.NodeItem<*> -> {
|
||||
val node = item.node
|
||||
rgbToString(
|
||||
node[RED_KEY].number?.toByte()?.toUByte() ?: 0u,
|
||||
node[GREEN_KEY].number?.toByte()?.toUByte() ?: 0u,
|
||||
node[BLUE_KEY].number?.toByte()?.toUByte() ?: 0u
|
||||
)
|
||||
}
|
||||
is MetaItem.ValueItem -> {
|
||||
if (item.value.type == ValueType.NUMBER) {
|
||||
rgbToString(item.value.int)
|
||||
} else {
|
||||
item.value.string
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun rgbToString(rgb: Int): String {
|
||||
val string = rgb.toString(16).padStart(6, '0')
|
||||
return "#" + string.substring(max(0, string.length - 6))
|
||||
}
|
||||
|
||||
fun rgbToString(red: UByte, green: UByte, blue: UByte): String {
|
||||
fun colorToString(color: UByte): String{
|
||||
return color.toString(16).padStart(2,'0')
|
||||
fun colorToString(color: UByte): String {
|
||||
return color.toString(16).padStart(2, '0')
|
||||
}
|
||||
return buildString {
|
||||
append("#")
|
||||
@ -186,4 +220,10 @@ object Colors {
|
||||
append(colorToString(blue))
|
||||
}
|
||||
}
|
||||
|
||||
fun rgbToMeta(r: UByte, g: UByte, b: UByte): Meta = buildMeta {
|
||||
RED_KEY put r.toInt()
|
||||
GREEN_KEY put g.toInt()
|
||||
BLUE_KEY put b.toInt()
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
@file:UseSerializers(MetaSerializer::class)
|
||||
|
||||
package hep.dataforge.vis.common
|
||||
|
||||
import hep.dataforge.io.serialization.MetaSerializer
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.get
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.asName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
import kotlinx.serialization.UseSerializers
|
||||
|
||||
@Serializable
|
||||
class StyleSheet() {
|
||||
@Transient
|
||||
internal var owner: VisualObject? = null
|
||||
|
||||
constructor(owner: VisualObject) : this() {
|
||||
this.owner = owner
|
||||
}
|
||||
|
||||
private val styleMap = HashMap<String, Meta>()
|
||||
|
||||
val items: Map<String, Meta> get() = styleMap
|
||||
|
||||
operator fun get(key: String): Meta? {
|
||||
return styleMap[key] ?: (owner?.parent as? VisualGroup)?.styleSheet?.get(key)
|
||||
}
|
||||
|
||||
operator fun set(key: String, style: Meta?) {
|
||||
val oldStyle = styleMap[key]
|
||||
if (style == null) {
|
||||
styleMap.remove(key)
|
||||
} else {
|
||||
styleMap[key] = style
|
||||
}
|
||||
owner?.styleChanged(key, oldStyle, style)
|
||||
}
|
||||
}
|
||||
|
||||
private fun VisualObject.styleChanged(key: String, oldStyle: Meta?, newStyle: Meta?) {
|
||||
if (styles.contains(key)) {
|
||||
//TODO optimize set concatenation
|
||||
val tokens: Collection<Name> = ((oldStyle?.items?.keys ?: emptySet()) + (newStyle?.items?.keys ?: emptySet()))
|
||||
.map { it.asName() }
|
||||
tokens.forEach { parent?.propertyChanged(it, oldStyle?.get(it), newStyle?.get(it)) }
|
||||
}
|
||||
if (this is VisualGroup) {
|
||||
this.forEach { it.styleChanged(key, oldStyle, newStyle) }
|
||||
}
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
package hep.dataforge.vis.common
|
||||
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.MetaBuilder
|
||||
import hep.dataforge.meta.buildMeta
|
||||
import hep.dataforge.meta.seal
|
||||
import hep.dataforge.names.*
|
||||
import hep.dataforge.provider.Provider
|
||||
|
||||
@ -12,6 +14,8 @@ interface VisualGroup : Provider, Iterable<VisualObject>, VisualObject {
|
||||
|
||||
override val defaultTarget: String get() = VisualObject.TYPE
|
||||
|
||||
val styleSheet: StyleSheet
|
||||
|
||||
override fun provideTop(target: String): Map<Name, Any> =
|
||||
when (target) {
|
||||
VisualObject.TYPE -> children.flatMap { (key, value) ->
|
||||
@ -22,7 +26,7 @@ interface VisualGroup : Provider, Iterable<VisualObject>, VisualObject {
|
||||
}
|
||||
res.entries
|
||||
}.associate { it.toPair() }
|
||||
//TODO add styles
|
||||
STYLE_TARGET -> styleSheet.items.mapKeys { it.key.toName() }
|
||||
else -> emptyMap()
|
||||
}
|
||||
|
||||
@ -32,17 +36,6 @@ interface VisualGroup : Provider, Iterable<VisualObject>, VisualObject {
|
||||
*/
|
||||
override fun iterator(): Iterator<VisualObject> = children.values.iterator()
|
||||
|
||||
/**
|
||||
* Resolve style by its name
|
||||
* TODO change to Config?
|
||||
*/
|
||||
fun getStyle(name: Name): Meta?
|
||||
|
||||
/**
|
||||
* Add or replace style with given name
|
||||
*/
|
||||
fun addStyle(name: Name, meta: Meta, apply: Boolean = true)
|
||||
|
||||
operator fun get(name: Name): VisualObject? {
|
||||
return when {
|
||||
name.isEmpty() -> this
|
||||
@ -50,8 +43,30 @@ interface VisualGroup : Provider, Iterable<VisualObject>, VisualObject {
|
||||
else -> (children[name.first()!!] as? VisualGroup)?.get(name.cutFirst())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A fix for serialization bug that writes all proper parents inside the tree after deserialization
|
||||
*/
|
||||
fun attachChildren(){
|
||||
styleSheet.owner = this
|
||||
this.children.values.forEach {
|
||||
it.parent = this
|
||||
(it as? VisualGroup)?.attachChildren()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val STYLE_TARGET = "style"
|
||||
}
|
||||
}
|
||||
|
||||
fun VisualGroup.updateStyle(key: String, builder: MetaBuilder.() -> Unit) {
|
||||
val newStyle = styleSheet[key]?.let { buildMeta(it, builder) } ?: buildMeta(builder)
|
||||
styleSheet[key] = newStyle.seal()
|
||||
}
|
||||
|
||||
data class StyleRef(val group: VisualGroup, val styleName: Name)
|
||||
|
||||
val VisualGroup.isEmpty: Boolean get() = this.children.isEmpty()
|
||||
|
||||
interface MutableVisualGroup : VisualGroup {
|
@ -1,9 +1,6 @@
|
||||
package hep.dataforge.vis.common
|
||||
|
||||
import hep.dataforge.meta.Config
|
||||
import hep.dataforge.meta.Configurable
|
||||
import hep.dataforge.meta.Laminate
|
||||
import hep.dataforge.meta.MetaItem
|
||||
import hep.dataforge.meta.*
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.asName
|
||||
import hep.dataforge.names.toName
|
||||
@ -26,11 +23,6 @@ interface VisualObject : Configurable {
|
||||
@Transient
|
||||
var parent: VisualObject?
|
||||
|
||||
/**
|
||||
* Direct properties access
|
||||
*/
|
||||
val properties: Config?
|
||||
|
||||
/**
|
||||
* Set property for this object
|
||||
*/
|
||||
@ -42,9 +34,11 @@ interface VisualObject : Configurable {
|
||||
fun getProperty(name: Name, inherit: Boolean = true): MetaItem<*>?
|
||||
|
||||
/**
|
||||
* Manually trigger property changed event. If [name] is empty, notify that the whole object is changed
|
||||
* Trigger property invalidation event. If [name] is empty, notify that the whole object is changed
|
||||
*/
|
||||
fun propertyChanged(name: Name, before: MetaItem<*>? = null, after: MetaItem<*>? = null): Unit
|
||||
fun propertyChanged(name: Name, before: MetaItem<*>?, after: MetaItem<*>?): Unit
|
||||
|
||||
fun propertyInvalidated(name: Name) = propertyChanged(name, null, null)
|
||||
|
||||
/**
|
||||
* Add listener triggering on property change
|
||||
@ -57,11 +51,9 @@ interface VisualObject : Configurable {
|
||||
fun removeChangeListener(owner: Any?)
|
||||
|
||||
/**
|
||||
* List of names of styles applied to this object. Order matters.
|
||||
* List of names of styles applied to this object. Order matters. Not inherited
|
||||
*/
|
||||
var styles: List<Name>
|
||||
|
||||
fun findAllStyles(): Laminate = Laminate(styles.distinct().mapNotNull(::findStyle))
|
||||
var styles: List<String>
|
||||
|
||||
companion object {
|
||||
const val TYPE = "visual"
|
||||
@ -69,12 +61,42 @@ interface VisualObject : Configurable {
|
||||
|
||||
//const val META_KEY = "@meta"
|
||||
//const val TAGS_KEY = "@tags"
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
fun VisualObject.getProperty(key: String, inherit: Boolean = true): MetaItem<*>? = getProperty(key.toName(), inherit)
|
||||
fun VisualObject.setProperty(key: String, value: Any?) = setProperty(key.toName(), value)
|
||||
|
||||
fun VisualObject.applyStyle(name: String) {
|
||||
styles = styles + name.toName()
|
||||
}
|
||||
/**
|
||||
* Add style name to the list of styles to be resolved later. The style with given name does not necessary exist at the moment.
|
||||
*/
|
||||
fun VisualObject.useStyle(name: String) {
|
||||
styles = styles + name
|
||||
}
|
||||
|
||||
//private tailrec fun VisualObject.topGroup(): VisualGroup? {
|
||||
// val parent = this.parent
|
||||
// return if (parent == null) {
|
||||
// this as? VisualGroup
|
||||
// }
|
||||
// else {
|
||||
// parent.topGroup()
|
||||
// }
|
||||
//}
|
||||
//
|
||||
///**
|
||||
// * Add or update given style on a top-most reachable parent group and apply it to this object
|
||||
// */
|
||||
//fun VisualObject.useStyle(name: String, builder: MetaBuilder.() -> Unit) {
|
||||
// val styleName = name.toName()
|
||||
// topGroup()?.updateStyle(styleName, builder) ?: error("Can't find parent group for $this")
|
||||
// useStyle(styleName)
|
||||
//}
|
||||
|
||||
tailrec fun VisualObject.findStyle(name: String): Meta? =
|
||||
(this as? VisualGroup)?.styleSheet?.get(name) ?: parent?.findStyle(name)
|
||||
|
||||
fun VisualObject.findAllStyles(): Laminate = Laminate(styles.mapNotNull(::findStyle))
|
||||
|
||||
|
@ -1,32 +1,20 @@
|
||||
import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpack
|
||||
|
||||
plugins {
|
||||
id("scientifik.mpp")
|
||||
}
|
||||
|
||||
scientifik{
|
||||
withSerialization()
|
||||
}
|
||||
|
||||
kotlin {
|
||||
sourceSets {
|
||||
val commonMain by getting {
|
||||
dependencies {
|
||||
api(project(":dataforge-vis-spatial"))
|
||||
api("scientifik:gdml:0.1.3")
|
||||
}
|
||||
}
|
||||
val jsMain by getting {
|
||||
dependencies {
|
||||
api(project(":dataforge-vis-spatial"))
|
||||
//api("kotlin.js.externals:kotlin-js-jquery:3.2.0-0")
|
||||
api("scientifik:gdml:0.1.4")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks{
|
||||
val jsBrowserWebpack by getting(KotlinWebpack::class) {
|
||||
sourceMaps = false
|
||||
}
|
||||
}
|
||||
//tasks{
|
||||
// val jsBrowserWebpack by getting(KotlinWebpack::class) {
|
||||
// sourceMaps = false
|
||||
// }
|
||||
//}
|
@ -6,8 +6,8 @@ import hep.dataforge.meta.buildMeta
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.toName
|
||||
import hep.dataforge.vis.common.VisualObject
|
||||
import hep.dataforge.vis.common.applyStyle
|
||||
import hep.dataforge.vis.spatial.Material3D.Companion.COLOR_KEY
|
||||
import hep.dataforge.vis.common.useStyle
|
||||
import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_COLOR_KEY
|
||||
import hep.dataforge.vis.spatial.RotationOrder
|
||||
import hep.dataforge.vis.spatial.VisualGroup3D
|
||||
import hep.dataforge.vis.spatial.VisualObject3D
|
||||
@ -43,7 +43,7 @@ class GDMLTransformer(val root: GDML) {
|
||||
styleCache.getOrPut(name.toName()){
|
||||
buildMeta(builder)
|
||||
}
|
||||
applyStyle(name)
|
||||
useStyle(name)
|
||||
}
|
||||
|
||||
internal fun configureSolid(obj: VisualObject3D, parent: GDMLVolume, solid: GDMLSolid) {
|
||||
@ -52,7 +52,7 @@ class GDMLTransformer(val root: GDML) {
|
||||
val styleName = "material[${material.name}]"
|
||||
|
||||
obj.useStyle(styleName){
|
||||
COLOR_KEY to random.nextInt(0, Int.MAX_VALUE)
|
||||
MATERIAL_COLOR_KEY put random.nextInt(16777216)
|
||||
"gdml.material" put material.name
|
||||
}
|
||||
|
||||
@ -68,7 +68,7 @@ class GDMLTransformer(val root: GDML) {
|
||||
internal fun finalize(final: VisualGroup3D): VisualGroup3D {
|
||||
final.prototypes = proto
|
||||
styleCache.forEach {
|
||||
final.addStyle(it.key, it.value, false)
|
||||
final.styleSheet[it.key.toString()] = it.value
|
||||
}
|
||||
final.rotationOrder = RotationOrder.ZXY
|
||||
onFinish(this@GDMLTransformer)
|
||||
|
@ -244,4 +244,11 @@ fun GDML.toVisual(block: GDMLTransformer.() -> Unit = {}): VisualGroup3D {
|
||||
val context = GDMLTransformer(this).apply(block)
|
||||
|
||||
return context.finalize(volume(context, world))
|
||||
}
|
||||
|
||||
/**
|
||||
* Append gdml node to the group
|
||||
*/
|
||||
fun VisualGroup3D.gdml(gdml: GDML, key: String = "", transformer: GDMLTransformer.() -> Unit = {}) {
|
||||
set(key, gdml.toVisual(transformer))
|
||||
}
|
@ -5,11 +5,10 @@ import hep.dataforge.js.Application
|
||||
import hep.dataforge.js.objectTree
|
||||
import hep.dataforge.js.startApplication
|
||||
import hep.dataforge.names.NameToken
|
||||
import hep.dataforge.vis.spatial.Material3D.Companion.OPACITY_KEY
|
||||
import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_OPACITY_KEY
|
||||
import hep.dataforge.vis.spatial.Visual3DPlugin
|
||||
import hep.dataforge.vis.spatial.VisualGroup3D
|
||||
import hep.dataforge.vis.spatial.VisualObject3D
|
||||
import hep.dataforge.vis.spatial.attachChildren
|
||||
import hep.dataforge.vis.spatial.editor.propertyEditor
|
||||
import hep.dataforge.vis.spatial.editor.threeOutputConfig
|
||||
import hep.dataforge.vis.spatial.gdml.GDMLTransformer
|
||||
@ -106,7 +105,7 @@ private class GDMLDemoApp : Application {
|
||||
|| parent.physVolumes.isNotEmpty()
|
||||
) {
|
||||
useStyle("opaque") {
|
||||
OPACITY_KEY to 0.3
|
||||
MATERIAL_OPACITY_KEY put 0.3
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -154,7 +153,7 @@ private class GDMLDemoApp : Application {
|
||||
output.camera.layers.set(0)
|
||||
configElement.threeOutputConfig(output)
|
||||
//tree.visualObjectTree(visual, editor::propertyEditor)
|
||||
treeElement.objectTree(NameToken("World"),visual, editorElement::propertyEditor)
|
||||
treeElement.objectTree(NameToken("World"), visual, editorElement::propertyEditor)
|
||||
|
||||
|
||||
output.render(visual)
|
||||
|
@ -1,26 +0,0 @@
|
||||
package hep.dataforge.vis.spatial.gdml
|
||||
|
||||
import hep.dataforge.vis.spatial.Visual3DPlugin
|
||||
import hep.dataforge.vis.spatial.VisualGroup3D
|
||||
import nl.adaptivity.xmlutil.StAXReader
|
||||
import scientifik.gdml.GDML
|
||||
import java.io.File
|
||||
|
||||
|
||||
fun main() {
|
||||
val file = File("D:\\Work\\Projects\\gdml.kt\\gdml-source\\BM@N_coil.gdml")
|
||||
|
||||
val xmlReader = StAXReader(file.inputStream(), "UTF-8")
|
||||
val xml = GDML.format.parse(GDML.serializer(), xmlReader)
|
||||
val visual = xml.toVisual {
|
||||
lUnit = LUnit.CM
|
||||
}
|
||||
|
||||
//val meta = visual.toMeta()
|
||||
|
||||
val str = Visual3DPlugin.json.stringify(VisualGroup3D.serializer(), visual)
|
||||
|
||||
println(str)
|
||||
|
||||
//println(Json.indented.stringify(meta.toJson()))
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
package hep.dataforge.vis.spatial.gdml
|
||||
|
||||
import hep.dataforge.vis.spatial.Material3D
|
||||
import hep.dataforge.vis.spatial.Visual3DPlugin
|
||||
import hep.dataforge.vis.spatial.VisualGroup3D
|
||||
import hep.dataforge.vis.spatial.VisualObject3D
|
||||
import hep.dataforge.vis.spatial.transform.RemoveSingleChild
|
||||
import hep.dataforge.vis.spatial.transform.UnRef
|
||||
import nl.adaptivity.xmlutil.StAXReader
|
||||
import scientifik.gdml.GDML
|
||||
import java.io.File
|
||||
|
||||
fun main() {
|
||||
val file = File("D:\\Work\\Projects\\gdml.kt\\gdml-source\\cubes.gdml")
|
||||
|
||||
val xmlReader = StAXReader(file.inputStream(), "UTF-8")
|
||||
val xml = GDML.format.parse(GDML.serializer(), xmlReader)
|
||||
val visual = xml.toVisual {
|
||||
lUnit = LUnit.CM
|
||||
|
||||
solidConfiguration = { parent, solid ->
|
||||
if (parent.physVolumes.isNotEmpty()) {
|
||||
useStyle("opaque") {
|
||||
Material3D.OPACITY_KEY to 0.3
|
||||
VisualObject3D.LAYER_KEY to 2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(visual as? VisualGroup3D)?.let { UnRef(it) }?.let { RemoveSingleChild(it) }
|
||||
|
||||
val string = Visual3DPlugin.json.stringify(VisualGroup3D.serializer(), visual)
|
||||
|
||||
val tmpFile = File.createTempFile("dataforge-visual", ".json")
|
||||
|
||||
tmpFile.writeText(string)
|
||||
|
||||
println(tmpFile.canonicalPath)
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
package hep.dataforge.vis.spatial.gdml
|
||||
|
||||
import hep.dataforge.names.toName
|
||||
import hep.dataforge.vis.spatial.*
|
||||
|
||||
fun main() {
|
||||
val vis = VisualGroup3D().apply {
|
||||
val box = Box(100f, 100f, 20f).apply {
|
||||
color(0u, 0u, 255u)
|
||||
}
|
||||
proxy("some.name".toName(), box, "obj")
|
||||
}
|
||||
|
||||
val string = Visual3DPlugin.json.stringify(VisualGroup3D.serializer(),vis)
|
||||
println(string)
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
package hep.dataforge.vis.spatial.gdml
|
||||
|
||||
import hep.dataforge.vis.spatial.Material3D
|
||||
import hep.dataforge.vis.spatial.Visual3DPlugin
|
||||
import hep.dataforge.vis.spatial.VisualGroup3D
|
||||
import hep.dataforge.vis.spatial.opacity
|
||||
import hep.dataforge.vis.spatial.transform.RemoveSingleChild
|
||||
import hep.dataforge.vis.spatial.transform.UnRef
|
||||
import nl.adaptivity.xmlutil.StAXReader
|
||||
import scientifik.gdml.GDML
|
||||
import java.io.File
|
||||
|
||||
fun main() {
|
||||
val file = File("D:\\Work\\Projects\\gdml.kt\\gdml-source\\BM@N.gdml")
|
||||
|
||||
val xmlReader = StAXReader(file.inputStream(), "UTF-8")
|
||||
val xml = GDML.format.parse(GDML.serializer(), xmlReader)
|
||||
val visual = xml.toVisual {
|
||||
lUnit = LUnit.CM
|
||||
|
||||
volumeAction = { volume ->
|
||||
when {
|
||||
volume.name.startsWith("ecal01lay") -> GDMLTransformer.Action.REJECT
|
||||
else -> GDMLTransformer.Action.CACHE
|
||||
}
|
||||
}
|
||||
|
||||
solidConfiguration = { parent, solid ->
|
||||
if (parent.physVolumes.isNotEmpty()
|
||||
|| solid.name.startsWith("Coil")
|
||||
|| solid.name.startsWith("Yoke")
|
||||
|| solid.name.startsWith("Magnet")
|
||||
|| solid.name.startsWith("Pole")
|
||||
) {
|
||||
useStyle("opaque") {
|
||||
Material3D.OPACITY_KEY to 0.3
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// (visual as? VisualGroup3D)?.let { UnRef(it) }?.let { RemoveSingleChild(it) }
|
||||
|
||||
val string = Visual3DPlugin.json.stringify(VisualGroup3D.serializer(), visual)
|
||||
|
||||
val tmpFile = File.createTempFile("dataforge-visual", ".json")
|
||||
|
||||
tmpFile.writeText(string)
|
||||
|
||||
println(tmpFile.canonicalPath)
|
||||
|
||||
// val template = visual.getTemplate("volumes.ecal01mod".toName())
|
||||
// println(template)
|
||||
// visual.flatMap { (it as? VisualGroup3D) ?: listOf(it) }.forEach {
|
||||
// if(it.parent==null) error("")
|
||||
// }
|
||||
//readLine()
|
||||
//val meta = visual.toMeta()
|
||||
// val tmpFile = File.createTempFile("dataforge-visual", "json")
|
||||
//tmpFile.writeText(meta.toString())
|
||||
//println(tmpFile.absoluteFile)
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package hep.dataforge.vis.spatial.gdml
|
||||
|
||||
import hep.dataforge.vis.spatial.VisualGroup3D
|
||||
import nl.adaptivity.xmlutil.StAXReader
|
||||
import scientifik.gdml.GDML
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
|
||||
fun VisualGroup3D.gdml(file: Path, key: String = "", transformer: GDMLTransformer.() -> Unit = {}) {
|
||||
val xmlReader = StAXReader(Files.newInputStream(file), "UTF-8")
|
||||
val gdml = GDML.format.parse(GDML.serializer(), xmlReader)
|
||||
gdml(gdml, key, transformer)
|
||||
}
|
@ -5,10 +5,6 @@ plugins {
|
||||
id("org.openjfx.javafxplugin")
|
||||
}
|
||||
|
||||
scientifik {
|
||||
withSerialization()
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvm {
|
||||
withJava()
|
||||
@ -21,9 +17,13 @@ kotlin {
|
||||
}
|
||||
jvmMain {
|
||||
dependencies {
|
||||
api("org.fxyz3d:fxyz3d:0.5.2")
|
||||
implementation("org.fxyz3d:fxyz3d:0.5.2") {
|
||||
exclude(module = "slf4j-simple")
|
||||
}
|
||||
api("org.jetbrains.kotlinx:kotlinx-coroutines-javafx:${Scientifik.coroutinesVersion}")
|
||||
implementation("eu.mihosoft.vrl.jcsg:jcsg:0.5.7")
|
||||
implementation("eu.mihosoft.vrl.jcsg:jcsg:0.5.7") {
|
||||
exclude(module = "slf4j-simple")
|
||||
}
|
||||
}
|
||||
}
|
||||
jsMain {
|
||||
|
@ -1,4 +1,5 @@
|
||||
@file:UseSerializers(Point3DSerializer::class)
|
||||
|
||||
package hep.dataforge.vis.spatial
|
||||
|
||||
import hep.dataforge.io.serialization.ConfigSerializer
|
||||
@ -43,10 +44,9 @@ inline fun VisualGroup3D.composite(
|
||||
val children = group.filterIsInstance<VisualObject3D>()
|
||||
if (children.size != 2) error("Composite requires exactly two children")
|
||||
return Composite(type, children[0], children[1]).also {
|
||||
if (group.properties != null) {
|
||||
it.config.update(group.config)
|
||||
it.material = group.material
|
||||
}
|
||||
it.config.update(group.config)
|
||||
//it.material = group.material
|
||||
|
||||
it.position = group.position
|
||||
it.rotation = group.rotation
|
||||
it.scale = group.scale
|
||||
|
@ -9,7 +9,8 @@ import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.UseSerializers
|
||||
|
||||
@Serializable
|
||||
class Text3D(var text: String, var fontSize: Int) : AbstractVisualObject(), VisualObject3D {
|
||||
class Label3D(var text: String, var fontSize: Double, var fontFamily: String) : AbstractVisualObject(),
|
||||
VisualObject3D {
|
||||
@Serializable(ConfigSerializer::class)
|
||||
override var properties: Config? = null
|
||||
|
||||
@ -19,5 +20,11 @@ class Text3D(var text: String, var fontSize: Int) : AbstractVisualObject(), Visu
|
||||
|
||||
}
|
||||
|
||||
fun VisualGroup3D.text(text: String, fontSize: Int, name: String = "", action: Text3D.() -> Unit = {}) =
|
||||
Text3D(text, fontSize).apply(action).also { set(name, it) }
|
||||
fun VisualGroup3D.label(
|
||||
text: String,
|
||||
fontSize: Number = 20,
|
||||
fontFamily: String = "Arial",
|
||||
name: String = "",
|
||||
action: Label3D.() -> Unit = {}
|
||||
) =
|
||||
Label3D(text, fontSize.toDouble(), fontFamily).apply(action).also { set(name, it) }
|
@ -3,63 +3,72 @@ package hep.dataforge.vis.spatial
|
||||
import hep.dataforge.meta.*
|
||||
import hep.dataforge.names.asName
|
||||
import hep.dataforge.names.plus
|
||||
import hep.dataforge.vis.common.VisualObject
|
||||
import hep.dataforge.vis.spatial.Material3D.Companion.COLOR_KEY
|
||||
import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_KEY
|
||||
import hep.dataforge.vis.spatial.Material3D.Companion.OPACITY_KEY
|
||||
import hep.dataforge.vis.common.Colors
|
||||
import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_COLOR_KEY
|
||||
import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_OPACITY_KEY
|
||||
|
||||
class Material3D(override val config: Config) : Specific {
|
||||
|
||||
var color by string()
|
||||
var color by string(key = COLOR_KEY)
|
||||
|
||||
var opacity by float(1f)
|
||||
var specularColor by string()
|
||||
|
||||
var opacity by float(1f, key = OPACITY_KEY)
|
||||
|
||||
var wireframe by boolean(false, WIREFRAME_KEY)
|
||||
|
||||
companion object : Specification<Material3D> {
|
||||
override fun wrap(config: Config): Material3D = Material3D(config)
|
||||
|
||||
val MATERIAL_KEY = "material".asName()
|
||||
val COLOR_KEY = MATERIAL_KEY + "color"
|
||||
val SPECULAR_COLOR = MATERIAL_KEY + "specularColor"
|
||||
val OPACITY_KEY = MATERIAL_KEY + "opacity"
|
||||
internal val COLOR_KEY = "color".asName()
|
||||
val MATERIAL_COLOR_KEY = MATERIAL_KEY + COLOR_KEY
|
||||
val SPECULAR_COLOR ="specularColor".asName()
|
||||
internal val OPACITY_KEY = "opacity".asName()
|
||||
val MATERIAL_OPACITY_KEY = MATERIAL_KEY + OPACITY_KEY
|
||||
internal val WIREFRAME_KEY = "wireframe".asName()
|
||||
val MATERIAL_WIREFRAME_KEY = MATERIAL_KEY + WIREFRAME_KEY
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
fun VisualObject.color(rgb: String) {
|
||||
setProperty(COLOR_KEY, rgb)
|
||||
fun VisualObject3D.color(rgb: String) {
|
||||
setProperty(MATERIAL_COLOR_KEY, rgb)
|
||||
}
|
||||
|
||||
fun VisualObject.color(rgb: Int) {
|
||||
setProperty(COLOR_KEY, rgb)
|
||||
fun VisualObject3D.color(rgb: Int) {
|
||||
setProperty(MATERIAL_COLOR_KEY, rgb)
|
||||
}
|
||||
|
||||
fun VisualObject.color(r: UByte, g: UByte, b: UByte) = setProperty(
|
||||
COLOR_KEY,
|
||||
buildMeta {
|
||||
"red" put r.toInt()
|
||||
"green" put g.toInt()
|
||||
"blue" put b.toInt()
|
||||
}
|
||||
fun VisualObject3D.color(r: UByte, g: UByte, b: UByte) = setProperty(
|
||||
MATERIAL_COLOR_KEY,
|
||||
Colors.rgbToMeta(r, g, b)
|
||||
)
|
||||
|
||||
var VisualObject.color: String?
|
||||
get() = getProperty(COLOR_KEY).string
|
||||
/**
|
||||
* Web colors representation of the color in `#rrggbb` format or HTML name
|
||||
*/
|
||||
var VisualObject3D.color: String?
|
||||
get() = getProperty(MATERIAL_COLOR_KEY)?.let { Colors.fromMeta(it) }
|
||||
set(value) {
|
||||
if (value != null) {
|
||||
color(value)
|
||||
}
|
||||
setProperty(MATERIAL_COLOR_KEY, value)
|
||||
}
|
||||
|
||||
var VisualObject.material: Material3D?
|
||||
get() = getProperty(MATERIAL_KEY).node?.let { Material3D.wrap(it) }
|
||||
set(value) = setProperty(MATERIAL_KEY, value?.config)
|
||||
//var VisualObject3D.material: Material3D?
|
||||
// get() = getProperty(MATERIAL_KEY).node?.let { Material3D.wrap(it) }
|
||||
// set(value) = setProperty(MATERIAL_KEY, value?.config)
|
||||
|
||||
fun VisualObject.material(builder: Material3D.() -> Unit) {
|
||||
material = Material3D.build(builder)
|
||||
fun VisualObject3D.material(builder: Material3D.() -> Unit) {
|
||||
val node = config[Material3D.MATERIAL_KEY].node
|
||||
if (node != null) {
|
||||
Material3D.update(node, builder)
|
||||
} else {
|
||||
config[Material3D.MATERIAL_KEY] = Material3D.build(builder)
|
||||
}
|
||||
}
|
||||
|
||||
var VisualObject.opacity: Double?
|
||||
get() = getProperty(OPACITY_KEY).double
|
||||
var VisualObject3D.opacity: Double?
|
||||
get() = getProperty(MATERIAL_OPACITY_KEY).double
|
||||
set(value) {
|
||||
setProperty(OPACITY_KEY, value)
|
||||
setProperty(MATERIAL_OPACITY_KEY, value)
|
||||
}
|
@ -5,17 +5,13 @@ package hep.dataforge.vis.spatial
|
||||
import hep.dataforge.io.serialization.ConfigSerializer
|
||||
import hep.dataforge.io.serialization.NameSerializer
|
||||
import hep.dataforge.meta.Config
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.MetaItem
|
||||
import hep.dataforge.meta.get
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.NameToken
|
||||
import hep.dataforge.names.asName
|
||||
import hep.dataforge.names.plus
|
||||
import hep.dataforge.vis.common.AbstractVisualObject
|
||||
import hep.dataforge.vis.common.MutableVisualGroup
|
||||
import hep.dataforge.vis.common.VisualGroup
|
||||
import hep.dataforge.vis.common.VisualObject
|
||||
import hep.dataforge.vis.common.*
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
import kotlinx.serialization.UseSerializers
|
||||
@ -43,12 +39,8 @@ class Proxy(val templateName: Name) : AbstractVisualObject(), VisualGroup, Visua
|
||||
get() = (parent as? VisualGroup3D)?.getPrototype(templateName)
|
||||
?: error("Template with name $templateName not found in $parent")
|
||||
|
||||
override fun getStyle(name: Name): Meta? = (parent as VisualGroup?)?.getStyle(name)
|
||||
|
||||
override fun addStyle(name: Name, meta: Meta, apply: Boolean) {
|
||||
(parent as VisualGroup?)?.addStyle(name, meta, apply)
|
||||
//do nothing
|
||||
}
|
||||
override val styleSheet: StyleSheet
|
||||
get() = (parent as? VisualGroup)?.styleSheet ?: StyleSheet(this)
|
||||
|
||||
override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? {
|
||||
return if (inherit) {
|
||||
@ -77,16 +69,17 @@ class Proxy(val templateName: Name) : AbstractVisualObject(), VisualGroup, Visua
|
||||
return NameToken(PROXY_CHILD_PROPERTY_PREFIX, childName.toString()) + propertyName
|
||||
}
|
||||
|
||||
private fun prototypeFor(name: Name): VisualObject =
|
||||
(prototype as? VisualGroup)?.get(name)
|
||||
?: error("Prototype with name $name not found in ${this@Proxy}")
|
||||
private fun prototypeFor(name: Name): VisualObject {
|
||||
return (prototype as? VisualGroup)?.get(name)
|
||||
?: error("Prototype with name $name not found in $this")
|
||||
}
|
||||
|
||||
|
||||
override var styles: List<Name>
|
||||
override var styles: List<String>
|
||||
get() = super.styles + prototype.styles
|
||||
set(value) {
|
||||
setProperty(VisualObject.STYLE_KEY, value.map { it.toString() })
|
||||
styleChanged()
|
||||
setProperty(VisualObject.STYLE_KEY, value)
|
||||
updateStyles(value)
|
||||
}
|
||||
|
||||
//override fun findAllStyles(): Laminate = Laminate((styles + prototype.styles).mapNotNull { findStyle(it) })
|
||||
@ -94,9 +87,9 @@ class Proxy(val templateName: Name) : AbstractVisualObject(), VisualGroup, Visua
|
||||
@Serializable
|
||||
inner class ProxyChild(val name: Name) : AbstractVisualObject(), VisualGroup {
|
||||
|
||||
val prototype: VisualObject by lazy {
|
||||
prototypeFor(name)
|
||||
}
|
||||
val prototype: VisualObject get() = prototypeFor(name)
|
||||
|
||||
override val styleSheet: StyleSheet get() = this@Proxy.styleSheet
|
||||
|
||||
override val children: Map<NameToken, VisualObject>
|
||||
get() = (prototype as? VisualGroup)?.children?.mapValues { (key, _) ->
|
||||
@ -105,12 +98,6 @@ class Proxy(val templateName: Name) : AbstractVisualObject(), VisualGroup, Visua
|
||||
)
|
||||
} ?: emptyMap()
|
||||
|
||||
override fun getStyle(name: Name): Meta? = this@Proxy.getStyle(name)
|
||||
|
||||
override fun addStyle(name: Name, meta: Meta, apply: Boolean) {
|
||||
this@Proxy.addStyle(name, meta, apply)
|
||||
}
|
||||
|
||||
override var properties: Config?
|
||||
get() = propertyCache[name]
|
||||
set(value) {
|
||||
@ -176,8 +163,8 @@ fun VisualGroup3D.proxy(
|
||||
): Proxy {
|
||||
val existing = getPrototype(templateName)
|
||||
if (existing == null) {
|
||||
setPrototype(templateName,obj, attachToParent)
|
||||
} else if(existing != obj) {
|
||||
setPrototype(templateName, obj, attachToParent)
|
||||
} else if (existing != obj) {
|
||||
error("Can't add different prototype on top of existing one")
|
||||
}
|
||||
return ref(templateName, name, block)
|
||||
|
@ -12,12 +12,12 @@ import hep.dataforge.io.serialization.ConfigSerializer
|
||||
import hep.dataforge.io.serialization.MetaSerializer
|
||||
import hep.dataforge.io.serialization.NameSerializer
|
||||
import hep.dataforge.meta.Config
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.NameToken
|
||||
import hep.dataforge.names.asName
|
||||
import hep.dataforge.names.isEmpty
|
||||
import hep.dataforge.vis.common.AbstractVisualGroup
|
||||
import hep.dataforge.vis.common.StyleSheet
|
||||
import hep.dataforge.vis.common.VisualGroup
|
||||
import hep.dataforge.vis.common.VisualObject
|
||||
import kotlinx.serialization.SerialName
|
||||
@ -37,9 +37,10 @@ class VisualGroup3D : AbstractVisualGroup(), VisualObject3D {
|
||||
field = value
|
||||
}
|
||||
|
||||
override val styleSheet: StyleSheet = StyleSheet(this)
|
||||
|
||||
//FIXME to be lifted to AbstractVisualGroup after https://github.com/Kotlin/kotlinx.serialization/issues/378 is fixed
|
||||
override var properties: Config? = null
|
||||
override val styleSheet = HashMap<Name, Meta>()
|
||||
|
||||
override var position: Point3D? = null
|
||||
override var rotation: Point3D? = null
|
||||
@ -49,6 +50,11 @@ class VisualGroup3D : AbstractVisualGroup(), VisualObject3D {
|
||||
private val _children = HashMap<NameToken, VisualObject>()
|
||||
override val children: Map<NameToken, VisualObject> get() = _children
|
||||
|
||||
init {
|
||||
//Do after deserialization
|
||||
attachChildren()
|
||||
}
|
||||
|
||||
override fun removeChild(token: NameToken) {
|
||||
_children.remove(token)
|
||||
childrenChanged(token.asName(), null)
|
||||
@ -96,26 +102,18 @@ class VisualGroup3D : AbstractVisualGroup(), VisualObject3D {
|
||||
}
|
||||
}
|
||||
|
||||
override fun attachChildren() {
|
||||
super.attachChildren()
|
||||
prototypes?.run {
|
||||
parent = this
|
||||
attachChildren()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val PROTOTYPES_KEY = "templates"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A fix for serialization bug that writes all proper parents inside the tree after deserialization
|
||||
*/
|
||||
fun VisualGroup.attachChildren() {
|
||||
this.children.values.forEach {
|
||||
it.parent = this
|
||||
(it as? VisualGroup)?.attachChildren()
|
||||
}
|
||||
if (this is VisualGroup3D) {
|
||||
prototypes?.also {
|
||||
it.parent = this
|
||||
it.attachChildren()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun VisualGroup3D.group(key: String = "", action: VisualGroup3D.() -> Unit = {}): VisualGroup3D =
|
||||
VisualGroup3D().apply(action).also { set(key, it) }
|
@ -129,21 +129,21 @@ var VisualObject3D.x: Number
|
||||
get() = position?.x ?: 0f
|
||||
set(value) {
|
||||
position().x = value.toDouble()
|
||||
propertyChanged(VisualObject3D.xPos)
|
||||
propertyInvalidated(VisualObject3D.xPos)
|
||||
}
|
||||
|
||||
var VisualObject3D.y: Number
|
||||
get() = position?.y ?: 0f
|
||||
set(value) {
|
||||
position().y = value.toDouble()
|
||||
propertyChanged(VisualObject3D.yPos)
|
||||
propertyInvalidated(VisualObject3D.yPos)
|
||||
}
|
||||
|
||||
var VisualObject3D.z: Number
|
||||
get() = position?.z ?: 0f
|
||||
set(value) {
|
||||
position().z = value.toDouble()
|
||||
propertyChanged(VisualObject3D.zPos)
|
||||
propertyInvalidated(VisualObject3D.zPos)
|
||||
}
|
||||
|
||||
private fun VisualObject3D.rotation(): Point3D =
|
||||
@ -153,21 +153,21 @@ var VisualObject3D.rotationX: Number
|
||||
get() = rotation?.x ?: 0f
|
||||
set(value) {
|
||||
rotation().x = value.toDouble()
|
||||
propertyChanged(VisualObject3D.xRotation)
|
||||
propertyInvalidated(VisualObject3D.xRotation)
|
||||
}
|
||||
|
||||
var VisualObject3D.rotationY: Number
|
||||
get() = rotation?.y ?: 0f
|
||||
set(value) {
|
||||
rotation().y = value.toDouble()
|
||||
propertyChanged(VisualObject3D.yRotation)
|
||||
propertyInvalidated(VisualObject3D.yRotation)
|
||||
}
|
||||
|
||||
var VisualObject3D.rotationZ: Number
|
||||
get() = rotation?.z ?: 0f
|
||||
set(value) {
|
||||
rotation().z = value.toDouble()
|
||||
propertyChanged(VisualObject3D.zRotation)
|
||||
propertyInvalidated(VisualObject3D.zRotation)
|
||||
}
|
||||
|
||||
private fun VisualObject3D.scale(): Point3D =
|
||||
@ -177,19 +177,19 @@ var VisualObject3D.scaleX: Number
|
||||
get() = scale?.x ?: 1f
|
||||
set(value) {
|
||||
scale().x = value.toDouble()
|
||||
propertyChanged(VisualObject3D.xScale)
|
||||
propertyInvalidated(VisualObject3D.xScale)
|
||||
}
|
||||
|
||||
var VisualObject3D.scaleY: Number
|
||||
get() = scale?.y ?: 1f
|
||||
set(value) {
|
||||
scale().y = value.toDouble()
|
||||
propertyChanged(VisualObject3D.yScale)
|
||||
propertyInvalidated(VisualObject3D.yScale)
|
||||
}
|
||||
|
||||
var VisualObject3D.scaleZ: Number
|
||||
get() = scale?.z ?: 1f
|
||||
set(value) {
|
||||
scale().z = value.toDouble()
|
||||
propertyChanged(VisualObject3D.zScale)
|
||||
propertyInvalidated(VisualObject3D.zScale)
|
||||
}
|
@ -14,8 +14,8 @@ operator fun Point2D.component1() = x
|
||||
operator fun Point2D.component2() = y
|
||||
|
||||
fun Point2D.toMeta() = buildMeta {
|
||||
VisualObject3D.x to x
|
||||
VisualObject3D.y to y
|
||||
VisualObject3D.x put x
|
||||
VisualObject3D.y put y
|
||||
}
|
||||
|
||||
fun Meta.point2D() = Point2D(this["x"].number ?: 0, this["y"].number ?: 0)
|
||||
@ -53,7 +53,7 @@ fun Meta.point3D() = Point3D(this["x"].number ?: 0, this["y"].number ?: 0, this[
|
||||
val zero = Point3D(0, 0, 0)
|
||||
|
||||
fun Point3D.toMeta() = buildMeta {
|
||||
VisualObject3D.x to x
|
||||
VisualObject3D.y to y
|
||||
VisualObject3D.z to z
|
||||
VisualObject3D.x put x
|
||||
VisualObject3D.y put y
|
||||
VisualObject3D.z put z
|
||||
}
|
@ -10,7 +10,9 @@ import hep.dataforge.vis.spatial.*
|
||||
internal fun mergeChild(parent: VisualGroup, child: VisualObject): VisualObject {
|
||||
return child.apply {
|
||||
|
||||
parent.properties?.let { config.update(it) }
|
||||
config.update(parent.config)
|
||||
|
||||
//parent.properties?.let { config.update(it) }
|
||||
|
||||
if (this is VisualObject3D && parent is VisualObject3D) {
|
||||
position += parent.position
|
||||
|
@ -0,0 +1,55 @@
|
||||
package hep.dataforge.vis.spatial
|
||||
|
||||
import hep.dataforge.meta.int
|
||||
import hep.dataforge.meta.set
|
||||
import hep.dataforge.names.asName
|
||||
import hep.dataforge.vis.common.updateStyle
|
||||
import hep.dataforge.vis.common.useStyle
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class PropertyTest {
|
||||
@Test
|
||||
fun testInheritedProperty(){
|
||||
var box: Box? = null
|
||||
val group = VisualGroup3D().apply {
|
||||
config["test"] = 22
|
||||
group {
|
||||
box = box(100,100,100)
|
||||
}
|
||||
}
|
||||
assertEquals(22, box?.getProperty("test".asName()).int)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testStyleProperty(){
|
||||
var box: Box? = null
|
||||
val group = VisualGroup3D().apply {
|
||||
updateStyle("testStyle"){
|
||||
"test" put 22
|
||||
}
|
||||
group {
|
||||
box = box(100,100,100).apply {
|
||||
useStyle("testStyle")
|
||||
}
|
||||
}
|
||||
}
|
||||
assertEquals(22, box?.getProperty("test".asName()).int)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testColor(){
|
||||
var box: Box? = null
|
||||
val group = VisualGroup3D().apply {
|
||||
updateStyle("testStyle"){
|
||||
Material3D.MATERIAL_COLOR_KEY put "#555555"
|
||||
}
|
||||
group {
|
||||
box = box(100,100,100){
|
||||
useStyle("testStyle")
|
||||
}
|
||||
}
|
||||
}
|
||||
assertEquals("#555555", box?.color)
|
||||
}
|
||||
}
|
@ -5,13 +5,10 @@ import hep.dataforge.js.jsObject
|
||||
import hep.dataforge.meta.*
|
||||
import hep.dataforge.vis.common.VisualObject
|
||||
import hep.dataforge.vis.common.findStyle
|
||||
import hep.dataforge.vis.spatial.Material3D.Companion.COLOR_KEY
|
||||
import hep.dataforge.vis.spatial.Material3D.Companion.OPACITY_KEY
|
||||
import hep.dataforge.vis.spatial.*
|
||||
import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_COLOR_KEY
|
||||
import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_OPACITY_KEY
|
||||
import hep.dataforge.vis.spatial.VisualObject3D.Companion.VISIBLE_KEY
|
||||
import hep.dataforge.vis.spatial.color
|
||||
import hep.dataforge.vis.spatial.opacity
|
||||
import hep.dataforge.vis.spatial.prototype
|
||||
import hep.dataforge.vis.spatial.visible
|
||||
import kotlinx.html.dom.append
|
||||
import kotlinx.html.js.div
|
||||
import kotlinx.html.js.h4
|
||||
@ -27,11 +24,17 @@ fun Element.propertyEditor(item: VisualObject?) {
|
||||
if (item != null) {
|
||||
append {
|
||||
card("Properties") {
|
||||
val config = (item.properties ?: item.prototype?.properties) ?: EmptyMeta
|
||||
val config: Meta = if (item is Proxy || item is Proxy.ProxyChild) {
|
||||
item.prototype?.config ?: EmptyMeta
|
||||
} else {
|
||||
item.config
|
||||
}
|
||||
val metaToEdit = config.builder().apply {
|
||||
VISIBLE_KEY to (item.visible ?: true)
|
||||
COLOR_KEY to (item.color ?: "#ffffff")
|
||||
OPACITY_KEY to (item.opacity ?: 1.0)
|
||||
if (item is VisualObject3D) {
|
||||
MATERIAL_COLOR_KEY to (item.color ?: "#ffffff")
|
||||
MATERIAL_OPACITY_KEY to (item.opacity ?: 1.0)
|
||||
}
|
||||
}
|
||||
val dMeta: dynamic = metaToEdit.toDynamic()
|
||||
val options: JSONEditorOptions = jsObject {
|
||||
|
@ -35,7 +35,7 @@ abstract class MeshThreeFactory<in T : VisualObject3D>(
|
||||
//JS sometimes tries to pass Geometry as BufferGeometry
|
||||
@Suppress("USELESS_IS_CHECK") if (geometry !is BufferGeometry) error("BufferGeometry expected")
|
||||
|
||||
val meshMeta: Meta = obj.properties[Material3D.MATERIAL_KEY]?.node ?: Meta.empty
|
||||
//val meshMeta: Meta = obj.properties[Material3D.MATERIAL_KEY]?.node ?: Meta.empty
|
||||
|
||||
val mesh = Mesh(geometry, MeshBasicMaterial()).apply {
|
||||
matrixAutoUpdate = false
|
||||
|
@ -11,7 +11,6 @@ import info.laht.threekt.materials.MeshBasicMaterial
|
||||
import info.laht.threekt.materials.MeshPhongMaterial
|
||||
import info.laht.threekt.math.Color
|
||||
import info.laht.threekt.objects.Mesh
|
||||
import kotlin.math.max
|
||||
|
||||
|
||||
object ThreeMaterials {
|
||||
@ -46,12 +45,6 @@ object ThreeMaterials {
|
||||
linewidth = meta["thickness"].double ?: 1.0
|
||||
}
|
||||
}
|
||||
|
||||
fun rgbToString(rgb: Int): String {
|
||||
val string = rgb.toString(16).padStart(6, '0')
|
||||
return "#" + string.substring(max(0, string.length - 6))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -67,9 +60,9 @@ fun MetaItem<*>.color(): Color {
|
||||
}
|
||||
is MetaItem.NodeItem -> {
|
||||
Color(
|
||||
node["red"]?.int ?: 0,
|
||||
node["green"]?.int ?: 0,
|
||||
node["blue"]?.int ?: 0
|
||||
node[Colors.RED_KEY]?.int ?: 0,
|
||||
node[Colors.GREEN_KEY]?.int ?: 0,
|
||||
node[Colors.BLUE_KEY]?.int ?: 0
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -99,11 +92,12 @@ fun MetaItem<*>.color(): Color {
|
||||
//fun Material3D?.jsLineMaterial(): Material = this?.config.jsLineMaterial()
|
||||
|
||||
fun Mesh.updateMaterial(obj: VisualObject) {
|
||||
val meta = obj.properties[Material3D.MATERIAL_KEY].node?:EmptyMeta
|
||||
val meta = obj.getProperty(Material3D.MATERIAL_KEY).node?:EmptyMeta
|
||||
material = (material as? MeshBasicMaterial ?: MeshBasicMaterial()).apply {
|
||||
color = meta["color"]?.color() ?: ThreeMaterials.DEFAULT_COLOR
|
||||
opacity = meta["opacity"]?.double ?: 1.0
|
||||
transparent = meta["transparent"].boolean ?: (opacity < 1.0)
|
||||
color = meta[Material3D.COLOR_KEY]?.color() ?: ThreeMaterials.DEFAULT_COLOR
|
||||
opacity = meta[Material3D.OPACITY_KEY]?.double ?: 1.0
|
||||
transparent = opacity < 1.0
|
||||
wireframe = meta[Material3D.WIREFRAME_KEY].boolean?:false
|
||||
needsUpdate = true
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ class ThreePlugin : AbstractPlugin() {
|
||||
objectFactories[Sphere::class] = ThreeSphereFactory
|
||||
objectFactories[ConeSegment::class] = ThreeCylinderFactory
|
||||
objectFactories[PolyLine::class] = ThreeLineFactory
|
||||
objectFactories[Label3D::class] = ThreeTextFactory
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
|
@ -0,0 +1,47 @@
|
||||
package hep.dataforge.vis.spatial.three
|
||||
|
||||
import hep.dataforge.vis.spatial.Label3D
|
||||
import info.laht.threekt.DoubleSide
|
||||
import info.laht.threekt.core.Object3D
|
||||
import info.laht.threekt.geometries.PlaneGeometry
|
||||
import info.laht.threekt.materials.MeshBasicMaterial
|
||||
import info.laht.threekt.objects.Mesh
|
||||
import info.laht.threekt.textures.Texture
|
||||
import org.w3c.dom.CanvasRenderingContext2D
|
||||
import org.w3c.dom.HTMLCanvasElement
|
||||
import kotlin.browser.document
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/**
|
||||
* Using example from http://stemkoski.github.io/Three.js/Texture-From-Canvas.html
|
||||
*/
|
||||
object ThreeTextFactory : ThreeFactory<Label3D> {
|
||||
override val type: KClass<in Label3D> get() = Label3D::class
|
||||
|
||||
override fun invoke(obj: Label3D): Object3D {
|
||||
val canvas = document.createElement("canvas") as HTMLCanvasElement
|
||||
val context = canvas.getContext("2d") as CanvasRenderingContext2D
|
||||
context.font = "${obj.fontSize}pt ${obj.fontFamily}"
|
||||
context.fillStyle = "rgba(255,0,0,0.95)"//obj.material?.color ?: "black"
|
||||
context.fillText(obj.text, 0.0, 0.0)
|
||||
|
||||
// canvas contents will be used for a texture
|
||||
val texture = Texture(canvas)
|
||||
texture.needsUpdate = true
|
||||
|
||||
val material = MeshBasicMaterial().apply {
|
||||
map = texture
|
||||
side = DoubleSide
|
||||
}
|
||||
material.transparent = true;
|
||||
|
||||
val mesh = Mesh(
|
||||
PlaneGeometry(canvas.clientWidth, canvas.clientHeight),
|
||||
material
|
||||
)
|
||||
|
||||
mesh.updatePosition(obj)
|
||||
|
||||
return mesh
|
||||
}
|
||||
}
|
@ -1,211 +0,0 @@
|
||||
package hep.dataforge.vis.spatial.demo
|
||||
|
||||
import hep.dataforge.vis.common.Colors
|
||||
import hep.dataforge.vis.spatial.*
|
||||
import javafx.stage.Stage
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.javafx.JavaFx
|
||||
import tornadofx.*
|
||||
import kotlin.math.PI
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
import kotlin.random.Random
|
||||
|
||||
class FXDemoApp : App(FXDemoGrid::class) {
|
||||
|
||||
val view: FXDemoGrid by inject()
|
||||
|
||||
override fun start(stage: Stage) {
|
||||
super.start(stage)
|
||||
view.run {
|
||||
demo("shapes", "Basic shapes") {
|
||||
box(100.0, 100.0, 100.0) {
|
||||
z = 110.0
|
||||
}
|
||||
sphere(50.0) {
|
||||
x = 110
|
||||
detail = 16
|
||||
}
|
||||
tube(50, height = 10, innerRadius = 25, angle = PI) {
|
||||
y = 110
|
||||
detail = 16
|
||||
rotationX = PI / 4
|
||||
}
|
||||
}
|
||||
|
||||
demo("dynamic", "Dynamic properties") {
|
||||
val group = group {
|
||||
box(100, 100, 100) {
|
||||
z = 110.0
|
||||
}
|
||||
|
||||
box(100, 100, 100) {
|
||||
visible = false
|
||||
x = 110.0
|
||||
//override color for this cube
|
||||
color(1530)
|
||||
|
||||
GlobalScope.launch(Dispatchers.JavaFx) {
|
||||
while (isActive) {
|
||||
delay(500)
|
||||
visible = !(visible ?: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GlobalScope.launch(Dispatchers.JavaFx) {
|
||||
val random = Random(111)
|
||||
while (isActive) {
|
||||
delay(1000)
|
||||
group.color(random.nextInt(0, Int.MAX_VALUE))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
demo("rotation", "Rotations") {
|
||||
box(100, 100, 100)
|
||||
group {
|
||||
x = 200
|
||||
rotationY = PI / 4
|
||||
box(100, 100, 100) {
|
||||
rotationZ = PI / 4
|
||||
color(Colors.red)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
demo("extrude", "extruded shape") {
|
||||
extrude {
|
||||
shape {
|
||||
polygon(8, 50)
|
||||
}
|
||||
for (i in 0..100) {
|
||||
layer(i * 5, 20 * sin(2 * PI / 100 * i), 20 * cos(2 * PI / 100 * i))
|
||||
}
|
||||
color(Colors.teal)
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
// }
|
||||
// composite(CompositeType.SUBTRACT) {
|
||||
// y = -300
|
||||
// box(100, 100, 100) {
|
||||
// z = 50
|
||||
// }
|
||||
// sphere(50)
|
||||
// color(Colors.blue)
|
||||
// }
|
||||
// }
|
||||
|
||||
// demo("CSG.custom", "CSG with manually created object") {
|
||||
// intersect {
|
||||
// box(100, 100, 100)
|
||||
// tube(60, 10) {
|
||||
// detail = 180
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
demo("lines", "Track / line segments") {
|
||||
sphere(100) {
|
||||
color(Colors.blue)
|
||||
detail = 50
|
||||
opacity = 0.4
|
||||
}
|
||||
repeat(20) {
|
||||
polyline(Point3D(100, 100, 100), Point3D(-100, -100, -100)) {
|
||||
thickness = 208.0
|
||||
rotationX = it * PI2 / 20
|
||||
color(Colors.green)
|
||||
//rotationY = it * PI2 / 20
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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(200)
|
||||
// boxes.forEach { box ->
|
||||
// box.value = (box.value + Random.nextInt(-15, 15)).coerceIn(0..255)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//class SpatialDemoView : View() {
|
||||
// private val plugin = Global.plugins.fetch(FX3DPlugin)
|
||||
// private val canvas = FXCanvas3D(plugin)
|
||||
//
|
||||
// override val root: Parent = borderpane {
|
||||
// center = canvas.root
|
||||
// }
|
||||
//
|
||||
// lateinit var group: VisualGroup3D
|
||||
//
|
||||
// init {
|
||||
// canvas.render {
|
||||
// group = group {
|
||||
// box(100, 100, 100)
|
||||
// box(100, 100, 100) {
|
||||
// x = 110.0
|
||||
// color(Colors.blue)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// //var color by group.config.number(1530)
|
||||
//
|
||||
// GlobalScope.launch {
|
||||
// val random = Random(111)
|
||||
// while (isActive) {
|
||||
// delay(1000)
|
||||
// group.color(random.nextInt(0, Int.MAX_VALUE))
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//// canvas.apply {
|
||||
//// angleY = -30.0
|
||||
//// angleX = -15.0
|
||||
//// }
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
fun main() {
|
||||
launch<FXDemoApp>()
|
||||
}
|
@ -2,13 +2,20 @@ package hep.dataforge.vis.spatial.fx
|
||||
|
||||
import hep.dataforge.context.*
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.boolean
|
||||
import hep.dataforge.meta.get
|
||||
import hep.dataforge.provider.Type
|
||||
import hep.dataforge.vis.spatial.*
|
||||
import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_KEY
|
||||
import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_WIREFRAME_KEY
|
||||
import hep.dataforge.vis.spatial.fx.FX3DFactory.Companion.TYPE
|
||||
import javafx.scene.Group
|
||||
import javafx.scene.Node
|
||||
import javafx.scene.shape.CullFace
|
||||
import javafx.scene.shape.DrawMode
|
||||
import javafx.scene.shape.Shape3D
|
||||
import javafx.scene.text.Font
|
||||
import javafx.scene.text.Text
|
||||
import javafx.scene.transform.Rotate
|
||||
import org.fxyz3d.shapes.composites.PolyLine3D
|
||||
import org.fxyz3d.shapes.primitives.CuboidMesh
|
||||
@ -56,11 +63,18 @@ class FX3DPlugin : AbstractPlugin() {
|
||||
} else {
|
||||
FXShapeFactory(obj, binding)
|
||||
}
|
||||
is Label3D -> Text(obj.text).apply {
|
||||
font = Font.font(obj.fontFamily, obj.fontSize)
|
||||
x = -layoutBounds.width / 2
|
||||
y = layoutBounds.height / 2
|
||||
}
|
||||
is PolyLine -> PolyLine3D(
|
||||
obj.points.map { it.point },
|
||||
obj.thickness.toFloat(),
|
||||
obj.material?.get("color")?.color()
|
||||
)
|
||||
obj.getProperty(Material3D.MATERIAL_COLOR_KEY)?.color()
|
||||
).apply {
|
||||
this.meshView.cullFace = CullFace.FRONT
|
||||
}
|
||||
else -> {
|
||||
//find specialized factory for this type if it is present
|
||||
val factory: FX3DFactory<VisualObject3D>? = findObjectFactory(obj::class)
|
||||
@ -79,15 +93,15 @@ class FX3DPlugin : AbstractPlugin() {
|
||||
scaleZProperty().bind(binding[VisualObject3D.zScale].float(obj.scaleZ.toFloat()))
|
||||
|
||||
val rotateX = Rotate(0.0, Rotate.X_AXIS).apply {
|
||||
angleProperty().bind(binding[VisualObject3D.xRotation].float(obj.rotationX.toFloat()))
|
||||
angleProperty().bind(binding[VisualObject3D.xRotation].float(obj.rotationX.toFloat()).multiply(180.0 / PI))
|
||||
}
|
||||
|
||||
val rotateY = Rotate(0.0, Rotate.Y_AXIS).apply {
|
||||
angleProperty().bind(binding[VisualObject3D.yRotation].float(obj.rotationY.toFloat()))
|
||||
angleProperty().bind(binding[VisualObject3D.yRotation].float(obj.rotationY.toFloat()).multiply(180.0 / PI))
|
||||
}
|
||||
|
||||
val rotateZ = Rotate(0.0, Rotate.Z_AXIS).apply {
|
||||
angleProperty().bind(binding[VisualObject3D.zRotation].float(obj.rotationZ.toFloat()))
|
||||
angleProperty().bind(binding[VisualObject3D.zRotation].float(obj.rotationZ.toFloat()).multiply(180.0 / PI))
|
||||
}
|
||||
|
||||
when (obj.rotationOrder) {
|
||||
@ -100,9 +114,17 @@ class FX3DPlugin : AbstractPlugin() {
|
||||
}
|
||||
|
||||
if (this is Shape3D) {
|
||||
materialProperty().bind(binding[Material3D.MATERIAL_KEY].transform {
|
||||
materialProperty().bind(binding[MATERIAL_KEY].transform {
|
||||
it.material()
|
||||
})
|
||||
|
||||
drawModeProperty().bind(binding[MATERIAL_WIREFRAME_KEY].transform {
|
||||
if (it.boolean == true) {
|
||||
DrawMode.LINE
|
||||
} else {
|
||||
DrawMode.FILL
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -33,13 +33,15 @@ class FXCanvas3D(val plugin: FX3DPlugin, meta: Meta = EmptyMeta) :
|
||||
it.setRadius(meta["axis.width"].double ?: LINE_WIDTH)
|
||||
it.isVisible = meta["axis.visible"].boolean ?: (meta["axis"] != null)
|
||||
world.add(it)
|
||||
|
||||
}
|
||||
|
||||
val light = AmbientLight()
|
||||
|
||||
private val camera = PerspectiveCamera().apply {
|
||||
nearClip = CAMERA_NEAR_CLIP
|
||||
farClip = CAMERA_FAR_CLIP
|
||||
translateZ = CAMERA_INITIAL_DISTANCE
|
||||
this.add(light)
|
||||
}
|
||||
|
||||
val cameraTransform = CameraTransformer().also {
|
||||
@ -60,20 +62,27 @@ class FXCanvas3D(val plugin: FX3DPlugin, meta: Meta = EmptyMeta) :
|
||||
val rotationZProperty get() = cameraTransform.rz.angleProperty()
|
||||
var angleZ by rotationZProperty
|
||||
|
||||
private val canvas = SubScene(
|
||||
Group(world, cameraTransform).apply { DepthTest.ENABLE },
|
||||
400.0,
|
||||
400.0,
|
||||
true,
|
||||
SceneAntialiasing.BALANCED
|
||||
).also { scene ->
|
||||
scene.fill = Color.GREY
|
||||
scene.camera = camera
|
||||
//id = "canvas"
|
||||
handleKeyboard(scene)
|
||||
handleMouse(scene)
|
||||
}
|
||||
|
||||
override val root = borderpane {
|
||||
center = SubScene(
|
||||
Group(world, cameraTransform).apply { DepthTest.ENABLE },
|
||||
1024.0,
|
||||
768.0,
|
||||
true,
|
||||
SceneAntialiasing.BALANCED
|
||||
).also { scene ->
|
||||
scene.fill = Color.GREY
|
||||
scene.camera = camera
|
||||
id = "canvas"
|
||||
handleKeyboard(scene)
|
||||
handleMouse(scene)
|
||||
}
|
||||
center = canvas
|
||||
}
|
||||
|
||||
init {
|
||||
canvas.widthProperty().bind(root.widthProperty())
|
||||
canvas.heightProperty().bind(root.heightProperty())
|
||||
}
|
||||
|
||||
|
||||
@ -170,6 +179,6 @@ class FXCanvas3D(val plugin: FX3DPlugin, meta: Meta = EmptyMeta) :
|
||||
private const val ROTATION_SPEED = 2.0
|
||||
private const val TRACK_SPEED = 6.0
|
||||
private const val RESIZE_SPEED = 50.0
|
||||
private const val LINE_WIDTH = 3.0
|
||||
private const val LINE_WIDTH = 1.0
|
||||
}
|
||||
}
|
@ -1,25 +1,57 @@
|
||||
package hep.dataforge.vis.spatial.fx
|
||||
|
||||
import eu.mihosoft.jcsg.CSG
|
||||
import eu.mihosoft.jcsg.Polygon
|
||||
import eu.mihosoft.vvecmath.Vector3d
|
||||
import hep.dataforge.vis.spatial.Composite
|
||||
import hep.dataforge.vis.spatial.CompositeType
|
||||
import javafx.scene.Group
|
||||
import javafx.scene.Node
|
||||
import javafx.scene.shape.MeshView
|
||||
import org.fxyz3d.utils.MeshUtils
|
||||
import javafx.scene.shape.TriangleMesh
|
||||
import javafx.scene.shape.VertexFormat
|
||||
import java.util.*
|
||||
import kotlin.collections.HashMap
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
class FXCompositeFactory(val plugin: FX3DPlugin) :
|
||||
FX3DFactory<Composite> {
|
||||
private fun MeshView.toCSG(): CSG {
|
||||
val mesh = this.mesh as TriangleMesh
|
||||
if (mesh.vertexFormat != VertexFormat.POINT_TEXCOORD) error("Not POINT_TEXCOORD")
|
||||
val polygons: MutableList<Polygon> = ArrayList()
|
||||
val faces = mesh.faces
|
||||
val points = mesh.points
|
||||
|
||||
val vectorCache = HashMap<Int, Vector3d>()
|
||||
fun getVector(index: Int) = vectorCache.getOrPut(index) {
|
||||
Vector3d.xyz(
|
||||
points[3 * index].toDouble(),
|
||||
points[3 * index + 1].toDouble(),
|
||||
points[3 * index + 2].toDouble()
|
||||
)
|
||||
}
|
||||
|
||||
for (i in 0 until faces.size() / 6) {
|
||||
val polygon = Polygon.fromPoints(
|
||||
getVector(faces[6 * i]),
|
||||
getVector(faces[6 * i + 2]),
|
||||
getVector(faces[6 * i + 4])
|
||||
)
|
||||
polygons.add(polygon)
|
||||
}
|
||||
|
||||
return CSG.fromPolygons(polygons)
|
||||
}
|
||||
|
||||
class FXCompositeFactory(val plugin: FX3DPlugin) : FX3DFactory<Composite> {
|
||||
override val type: KClass<in Composite>
|
||||
get() = Composite::class
|
||||
|
||||
override fun invoke(obj: Composite, binding: VisualObjectFXBinding): Node {
|
||||
val first = plugin.buildNode(obj.first) as? MeshView ?: error("Can't build node")
|
||||
val second = plugin.buildNode(obj.second) as? MeshView ?: error("Can't build node")
|
||||
val firstCSG = MeshUtils.mesh2CSG(first)
|
||||
val secondCSG = MeshUtils.mesh2CSG(second)
|
||||
val resultCSG = when(obj.compositeType){
|
||||
val firstCSG = first.toCSG()
|
||||
val secondCSG = second.toCSG()
|
||||
val resultCSG = when (obj.compositeType) {
|
||||
CompositeType.UNION -> firstCSG.union(secondCSG)
|
||||
CompositeType.INTERSECT -> firstCSG.intersect(secondCSG)
|
||||
CompositeType.SUBTRACT -> firstCSG.difference(secondCSG)
|
||||
@ -28,9 +60,9 @@ class FXCompositeFactory(val plugin: FX3DPlugin) :
|
||||
}
|
||||
}
|
||||
|
||||
internal fun CSG.toNode(): Node{
|
||||
internal fun CSG.toNode(): Node {
|
||||
val meshes = toJavaFXMesh().asMeshViews
|
||||
return if(meshes.size == 1){
|
||||
return if (meshes.size == 1) {
|
||||
meshes.first()
|
||||
} else {
|
||||
Group(meshes.map { it })
|
||||
|
@ -5,6 +5,8 @@ import hep.dataforge.meta.double
|
||||
import hep.dataforge.meta.get
|
||||
import hep.dataforge.meta.int
|
||||
import hep.dataforge.values.ValueType
|
||||
import hep.dataforge.vis.common.Colors
|
||||
import hep.dataforge.vis.spatial.Material3D
|
||||
import javafx.scene.paint.Color
|
||||
import javafx.scene.paint.Material
|
||||
import javafx.scene.paint.PhongMaterial
|
||||
@ -39,16 +41,16 @@ fun MetaItem<*>.color(opacity: Double = 1.0): Color {
|
||||
val red = int and 0x00ff0000 shr 16
|
||||
val green = int and 0x0000ff00 shr 8
|
||||
val blue = int and 0x000000ff
|
||||
Color.rgb(red, green, blue)
|
||||
Color.rgb(red, green, blue, opacity)
|
||||
} else {
|
||||
Color.web(this.value.string)
|
||||
}
|
||||
is MetaItem.NodeItem -> {
|
||||
Color.rgb(
|
||||
node["red"]?.int ?: 0,
|
||||
node["green"]?.int ?: 0,
|
||||
node["blue"]?.int ?: 0,
|
||||
node["opacity"]?.double ?: opacity
|
||||
node[Colors.RED_KEY]?.int ?: 0,
|
||||
node[Colors.GREEN_KEY]?.int ?: 0,
|
||||
node[Colors.BLUE_KEY]?.int ?: 0,
|
||||
node[Material3D.OPACITY_KEY]?.double ?: opacity
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -62,9 +64,9 @@ fun MetaItem<*>?.material(): Material {
|
||||
null -> FXMaterials.GREY
|
||||
is MetaItem.ValueItem -> PhongMaterial(color())
|
||||
is MetaItem.NodeItem -> PhongMaterial().apply {
|
||||
val opacity = node["opacity"].double ?: 1.0
|
||||
diffuseColor = node["color"]?.color(opacity) ?: Color.DARKGREY
|
||||
specularColor = node["specularColor"]?.color(opacity) ?: Color.WHITE
|
||||
val opacity = node[Material3D.OPACITY_KEY].double ?: 1.0
|
||||
diffuseColor = node[Material3D.COLOR_KEY]?.color(opacity) ?: Color.DARKGREY
|
||||
specularColor = node[Material3D.SPECULAR_COLOR]?.color(opacity) ?: Color.WHITE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import hep.dataforge.vis.spatial.Shape
|
||||
import javafx.scene.shape.Mesh
|
||||
import javafx.scene.shape.MeshView
|
||||
import javafx.scene.shape.TriangleMesh
|
||||
import javafx.scene.shape.VertexFormat
|
||||
import org.fxyz3d.geometry.Face3
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
@ -42,13 +43,16 @@ private class FXGeometryBuilder : GeometryBuilder<Mesh> {
|
||||
}
|
||||
|
||||
override fun build(): Mesh {
|
||||
val mesh = TriangleMesh()
|
||||
val mesh = TriangleMesh(VertexFormat.POINT_TEXCOORD)
|
||||
vertices.forEach {
|
||||
//TODO optimize copy
|
||||
mesh.points.addAll(it.x.toFloat(), it.y.toFloat(), it.z.toFloat())
|
||||
}
|
||||
|
||||
mesh.texCoords.addAll(0f, 0f)
|
||||
|
||||
faces.forEach {
|
||||
mesh.faces.addAll(it.p0, it.p1, it.p2)
|
||||
mesh.faces.addAll(it.p0, 0, it.p1, 0, it.p2, 0)
|
||||
}
|
||||
return mesh
|
||||
}
|
||||
|
@ -2,9 +2,10 @@ package hep.dataforge.vis.spatial.fx
|
||||
|
||||
import hep.dataforge.meta.*
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.isEmpty
|
||||
import hep.dataforge.names.startsWith
|
||||
import hep.dataforge.names.toName
|
||||
import hep.dataforge.vis.common.VisualObject
|
||||
import javafx.application.Platform
|
||||
import javafx.beans.binding.ObjectBinding
|
||||
import tornadofx.*
|
||||
|
||||
@ -16,12 +17,17 @@ class VisualObjectFXBinding(val obj: VisualObject) {
|
||||
|
||||
init {
|
||||
obj.onPropertyChange(this) { name, _, _ ->
|
||||
var currentName = name
|
||||
while(!currentName.isEmpty()) {
|
||||
//recursively update all upper level bindings
|
||||
bindings[currentName]?.invalidate()
|
||||
currentName = currentName.cutLast()
|
||||
bindings.filter { it.key.startsWith(name) }.forEach { entry ->
|
||||
Platform.runLater {
|
||||
entry.value.invalidate()
|
||||
}
|
||||
}
|
||||
// var currentName = name
|
||||
// while (!currentName.isEmpty()) {
|
||||
// //recursively update all upper level bindings
|
||||
// bindings[currentName]?.invalidate()
|
||||
// currentName = currentName.cutLast()
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,9 +52,9 @@ fun ObjectBinding<MetaItem<*>?>.long() = objectBinding { it.long }
|
||||
fun ObjectBinding<MetaItem<*>?>.node() = objectBinding { it.node }
|
||||
|
||||
fun ObjectBinding<MetaItem<*>?>.string(default: String) = stringBinding { it.string ?: default }
|
||||
fun ObjectBinding<MetaItem<*>?>.double(default: Double) = objectBinding { it.double ?: default }
|
||||
fun ObjectBinding<MetaItem<*>?>.float(default: Float) = objectBinding { it.float ?: default }
|
||||
fun ObjectBinding<MetaItem<*>?>.int(default: Int) = objectBinding { it.int ?: default }
|
||||
fun ObjectBinding<MetaItem<*>?>.long(default: Long) = objectBinding { it.long ?:default }
|
||||
fun ObjectBinding<MetaItem<*>?>.double(default: Double) = doubleBinding { it.double ?: default }
|
||||
fun ObjectBinding<MetaItem<*>?>.float(default: Float) = floatBinding { it.float ?: default }
|
||||
fun ObjectBinding<MetaItem<*>?>.int(default: Int) = integerBinding { it.int ?: default }
|
||||
fun ObjectBinding<MetaItem<*>?>.long(default: Long) = longBinding { it.long ?: default }
|
||||
|
||||
fun <T> ObjectBinding<MetaItem<*>?>.transform(transform: (MetaItem<*>) -> T) = objectBinding { it?.let(transform) }
|
||||
|
@ -28,10 +28,9 @@ rootProject.name = "dataforge-vis"
|
||||
include(
|
||||
":dataforge-vis-common",
|
||||
":wrappers",
|
||||
":dataforge-vis-fx",
|
||||
":dataforge-vis-spatial",
|
||||
":dataforge-vis-spatial-gdml",
|
||||
":spatial-js-demo"
|
||||
":spatial-demo"
|
||||
)
|
||||
|
||||
//if(file("../dataforge-core").exists()) {
|
||||
|
39
spatial-demo/build.gradle.kts
Normal file
39
spatial-demo/build.gradle.kts
Normal file
@ -0,0 +1,39 @@
|
||||
import org.openjfx.gradle.JavaFXOptions
|
||||
|
||||
plugins {
|
||||
id("scientifik.mpp")
|
||||
id("org.openjfx.javafxplugin")
|
||||
id("application")
|
||||
}
|
||||
|
||||
kotlin {
|
||||
|
||||
jvm {
|
||||
withJava()
|
||||
}
|
||||
|
||||
js {
|
||||
browser {
|
||||
webpackTask {
|
||||
sourceMaps = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
commonMain {
|
||||
dependencies {
|
||||
api(project(":dataforge-vis-spatial"))
|
||||
api(project(":dataforge-vis-spatial-gdml"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
application {
|
||||
mainClassName = "hep.dataforge.vis.spatial.demo.FXDemoAppKt"
|
||||
}
|
||||
|
||||
configure<JavaFXOptions> {
|
||||
modules("javafx.controls")
|
||||
}
|
@ -0,0 +1,158 @@
|
||||
package hep.dataforge.vis.spatial.demo
|
||||
|
||||
import hep.dataforge.meta.buildMeta
|
||||
import hep.dataforge.names.toName
|
||||
import hep.dataforge.output.OutputManager
|
||||
import hep.dataforge.vis.common.Colors
|
||||
import hep.dataforge.vis.common.VisualObject
|
||||
import hep.dataforge.vis.spatial.*
|
||||
import kotlinx.coroutines.*
|
||||
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: VisualGroup3D.() -> Unit) {
|
||||
val meta = buildMeta {
|
||||
"title" put title
|
||||
}
|
||||
val output = get(VisualObject::class, name.toName(), meta = meta)
|
||||
output.render(action = block)
|
||||
}
|
||||
|
||||
fun OutputManager.showcase() {
|
||||
demo("shapes", "Basic shapes") {
|
||||
box(100.0, 100.0, 100.0) {
|
||||
z = 110.0
|
||||
}
|
||||
sphere(50.0) {
|
||||
x = 110
|
||||
detail = 16
|
||||
}
|
||||
tube(50, height = 10, innerRadius = 25, angle = PI) {
|
||||
y = 110
|
||||
detail = 16
|
||||
rotationX = PI / 4
|
||||
}
|
||||
}
|
||||
|
||||
demo("dynamic", "Dynamic properties") {
|
||||
val group = group {
|
||||
box(100, 100, 100) {
|
||||
z = 110.0
|
||||
}
|
||||
|
||||
box(100, 100, 100) {
|
||||
visible = false
|
||||
x = 110.0
|
||||
//override color for this cube
|
||||
color(1530)
|
||||
|
||||
GlobalScope.launch(Dispatchers.Main) {
|
||||
while (isActive) {
|
||||
delay(500)
|
||||
visible = !(visible ?: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GlobalScope.launch(Dispatchers.Main) {
|
||||
val random = Random(111)
|
||||
while (isActive) {
|
||||
delay(1000)
|
||||
group.color(random.nextInt(0, Int.MAX_VALUE))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
demo("rotation", "Rotations") {
|
||||
box(100, 100, 100)
|
||||
group {
|
||||
x = 200
|
||||
rotationY = PI / 4
|
||||
box(100, 100, 100) {
|
||||
rotationZ = PI / 4
|
||||
color(Colors.red)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
demo("extrude", "extruded shape") {
|
||||
extrude {
|
||||
shape {
|
||||
polygon(8, 50)
|
||||
}
|
||||
for (i in 0..100) {
|
||||
layer(i * 5, 20 * sin(2 * PI / 100 * i), 20 * cos(2 * PI / 100 * i))
|
||||
}
|
||||
color(Colors.teal)
|
||||
}
|
||||
}
|
||||
|
||||
demo("lines", "Track / line segments") {
|
||||
sphere(100) {
|
||||
detail = 32
|
||||
opacity = 0.4
|
||||
color(Colors.blue)
|
||||
}
|
||||
repeat(20) {
|
||||
polyline(Point3D(100, 100, 100), Point3D(-100, -100, -100)) {
|
||||
thickness = 3.0
|
||||
rotationX = it * PI2 / 20
|
||||
color(Colors.green)
|
||||
//rotationY = it * PI2 / 20
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
demo("text", "Box with a label") {
|
||||
box(100, 100, 50) {
|
||||
opacity = 0.3
|
||||
}
|
||||
label("Hello, world!",fontSize = 15) {
|
||||
z = -26
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun OutputManager.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)
|
||||
}
|
||||
composite(CompositeType.SUBTRACT) {
|
||||
y = -300
|
||||
box(100, 100, 100) {
|
||||
z = 50
|
||||
}
|
||||
sphere(50)
|
||||
color(Colors.blue)
|
||||
}
|
||||
}
|
||||
|
||||
demo("CSG.custom", "CSG with manually created object") {
|
||||
intersect {
|
||||
tube(60, 10) {
|
||||
detail = 64
|
||||
}
|
||||
box(100, 100, 100)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
package hep.dataforge.vis.spatial.demo
|
||||
|
||||
import hep.dataforge.context.ContextBuilder
|
||||
import hep.dataforge.context.Global
|
||||
import hep.dataforge.js.Application
|
||||
import hep.dataforge.js.startApplication
|
||||
import hep.dataforge.vis.spatial.three.MeshThreeFactory.Companion.EDGES_ENABLED_KEY
|
||||
import hep.dataforge.vis.spatial.three.MeshThreeFactory.Companion.WIREFRAME_ENABLED_KEY
|
||||
import hep.dataforge.vis.spatial.three.ThreePlugin
|
||||
import hep.dataforge.vis.spatial.x
|
||||
import hep.dataforge.vis.spatial.y
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.browser.document
|
||||
import kotlin.random.Random
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
override fun dispose() = emptyMap<String, Any>()//mapOf("lines" put presenter.dispose())
|
||||
}
|
||||
|
||||
fun main() {
|
||||
startApplication(::ThreeDemoApp)
|
||||
}
|
@ -1,20 +1,13 @@
|
||||
package hep.dataforge.vis.spatial.demo
|
||||
|
||||
import hep.dataforge.context.AbstractPlugin
|
||||
import hep.dataforge.context.Context
|
||||
import hep.dataforge.context.PluginFactory
|
||||
import hep.dataforge.context.PluginTag
|
||||
import hep.dataforge.context.Global
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.buildMeta
|
||||
import hep.dataforge.meta.get
|
||||
import hep.dataforge.meta.string
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.toName
|
||||
import hep.dataforge.output.OutputManager
|
||||
import hep.dataforge.output.Renderer
|
||||
import hep.dataforge.vis.common.VisualObject
|
||||
import hep.dataforge.vis.spatial.VisualGroup3D
|
||||
import hep.dataforge.vis.spatial.render
|
||||
import hep.dataforge.vis.spatial.three.ThreeCanvas
|
||||
import hep.dataforge.vis.spatial.three.ThreePlugin
|
||||
import hep.dataforge.vis.spatial.three.output
|
||||
@ -25,31 +18,25 @@ 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(meta: Meta) : AbstractPlugin(meta), OutputManager {
|
||||
override val tag: PluginTag get() = Companion.tag
|
||||
class ThreeDemoGrid(element: Element, meta: Meta = Meta.empty) : OutputManager {
|
||||
|
||||
private val gridRoot = document.create.div("row")
|
||||
private val outputs: MutableMap<Name, ThreeCanvas> = HashMap()
|
||||
|
||||
init {
|
||||
require(ThreePlugin)
|
||||
}
|
||||
private val three = Global.plugins.fetch(ThreePlugin)
|
||||
|
||||
override fun attach(context: Context) {
|
||||
super.attach(context)
|
||||
val elementId = meta["elementID"].string ?: "canvas"
|
||||
val element = document.getElementById(elementId) ?: error("Element with id $elementId not found on page")
|
||||
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> {
|
||||
val three = context.plugins.get<ThreePlugin>()!!
|
||||
|
||||
return outputs.getOrPut(name) {
|
||||
if (type != VisualObject::class) error("Supports only DisplayObject")
|
||||
@ -63,7 +50,7 @@ class ThreeDemoGrid(meta: Meta) : AbstractPlugin(meta), OutputManager {
|
||||
gridRoot.append {
|
||||
span("border") {
|
||||
div("col-6") {
|
||||
div { id = "output-$name" }.also{
|
||||
div { id = "output-$name" }.also {
|
||||
output.attach(it)
|
||||
}
|
||||
hr()
|
||||
@ -75,20 +62,5 @@ class ThreeDemoGrid(meta: Meta) : AbstractPlugin(meta), OutputManager {
|
||||
output
|
||||
} as Renderer<T>
|
||||
}
|
||||
|
||||
companion object : PluginFactory<ThreeDemoGrid> {
|
||||
override val tag: PluginTag = PluginTag(group = "hep.dataforge", name = "vis.js.spatial.demo")
|
||||
|
||||
override val type: KClass<out ThreeDemoGrid> = ThreeDemoGrid::class
|
||||
|
||||
override fun invoke(meta: Meta,context: Context): ThreeDemoGrid = ThreeDemoGrid(meta)
|
||||
}
|
||||
}
|
||||
|
||||
fun ThreeDemoGrid.demo(name: String, title: String = name, block: VisualGroup3D.() -> Unit) {
|
||||
val meta = buildMeta {
|
||||
"title" put title
|
||||
}
|
||||
val output = get(VisualObject::class, name.toName(), meta = meta)
|
||||
output.render(action = block)
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package hep.dataforge.vis.spatial.demo
|
||||
|
||||
import hep.dataforge.vis.spatial.Material3D
|
||||
import hep.dataforge.vis.spatial.gdml.LUnit
|
||||
import hep.dataforge.vis.spatial.gdml.gdml
|
||||
import javafx.stage.Stage
|
||||
import tornadofx.*
|
||||
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 = 400.0
|
||||
stage.height = 400.0
|
||||
|
||||
//view.showcase()
|
||||
view.demo("gdml", "gdml") {
|
||||
gdml(Paths.get("D:\\Work\\Projects\\gdml.kt\\gdml-source\\cubes.gdml")) {
|
||||
lUnit = LUnit.CM
|
||||
|
||||
solidConfiguration = { parent, solid ->
|
||||
if (parent.physVolumes.isNotEmpty()) {
|
||||
useStyle("opaque") {
|
||||
Material3D.MATERIAL_OPACITY_KEY put 0.3
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//setProperty(Material3D.MATERIAL_WIREFRAME_KEY, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
launch<FXDemoApp>()
|
||||
}
|
@ -47,12 +47,4 @@ class FXDemoGrid : View(), OutputManager {
|
||||
} as Renderer<T>
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun FXDemoGrid.demo(name: String, title: String = name, block: VisualGroup3D.() -> Unit) {
|
||||
val meta = buildMeta {
|
||||
"title" put title
|
||||
}
|
||||
val output = get(VisualObject::class, name.toName(), meta = meta)
|
||||
output.render(action = block)
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
plugins {
|
||||
id("scientifik.js")
|
||||
//id("kotlin-dce-js")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(project(":dataforge-vis-spatial"))
|
||||
testImplementation(kotlin("test-js"))
|
||||
}
|
||||
|
||||
//kotlin{
|
||||
// target {
|
||||
// browser{
|
||||
// webpackTask {
|
||||
// sourceMaps = false
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
@ -1,179 +0,0 @@
|
||||
package hep.dataforge.vis.spatial.demo
|
||||
|
||||
import hep.dataforge.context.ContextBuilder
|
||||
import hep.dataforge.js.Application
|
||||
import hep.dataforge.js.startApplication
|
||||
import hep.dataforge.vis.common.Colors
|
||||
import hep.dataforge.vis.spatial.*
|
||||
import hep.dataforge.vis.spatial.three.MeshThreeFactory.Companion.EDGES_ENABLED_KEY
|
||||
import hep.dataforge.vis.spatial.three.MeshThreeFactory.Companion.WIREFRAME_ENABLED_KEY
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.math.PI
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
import kotlin.random.Random
|
||||
|
||||
private class ThreeDemoApp : Application {
|
||||
|
||||
override fun start(state: Map<String, Any>) {
|
||||
|
||||
//TODO replace by optimized builder after dataforge 0.1.3-dev-8
|
||||
val context = ContextBuilder("three-demo").build()
|
||||
|
||||
context.plugins.load(ThreeDemoGrid()).run {
|
||||
demo("shapes", "Basic shapes") {
|
||||
box(100.0, 100.0, 100.0) {
|
||||
z = 110.0
|
||||
}
|
||||
sphere(50.0) {
|
||||
x = 110
|
||||
detail = 16
|
||||
}
|
||||
tube(50, height = 10, innerRadius = 25, angle = PI) {
|
||||
y = 110
|
||||
detail = 16
|
||||
rotationX = PI / 4
|
||||
}
|
||||
}
|
||||
|
||||
demo("dynamic", "Dynamic properties") {
|
||||
val group = group {
|
||||
box(100, 100, 100) {
|
||||
z = 110.0
|
||||
}
|
||||
|
||||
box(100, 100, 100) {
|
||||
visible = false
|
||||
x = 110.0
|
||||
//override color for this cube
|
||||
color(1530)
|
||||
|
||||
GlobalScope.launch {
|
||||
while (isActive) {
|
||||
delay(500)
|
||||
visible = !(visible ?: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GlobalScope.launch {
|
||||
val random = Random(111)
|
||||
while (isActive) {
|
||||
delay(1000)
|
||||
group.color(random.nextInt(0, Int.MAX_VALUE))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
demo("rotation", "Rotations") {
|
||||
box(100, 100, 100)
|
||||
group {
|
||||
x = 200
|
||||
rotationY = PI / 4
|
||||
box(100, 100, 100) {
|
||||
rotationZ = PI / 4
|
||||
color(Colors.red)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
demo("extrude", "extruded shape") {
|
||||
extrude {
|
||||
shape {
|
||||
polygon(8, 50)
|
||||
}
|
||||
for (i in 0..100) {
|
||||
layer(i * 5, 20 * sin(2 * PI / 100 * i), 20 * cos(2 * PI / 100 * i))
|
||||
}
|
||||
color(Colors.teal)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
composite(CompositeType.SUBTRACT) {
|
||||
y = -300
|
||||
box(100, 100, 100) {
|
||||
z = 50
|
||||
}
|
||||
sphere(50)
|
||||
color(Colors.blue)
|
||||
}
|
||||
}
|
||||
demo("CSG.custom", "CSG with manually created object") {
|
||||
intersect {
|
||||
box(100, 100, 100)
|
||||
tube(60, 10) {
|
||||
detail = 180
|
||||
}
|
||||
}
|
||||
}
|
||||
demo("lines", "Track / line segments") {
|
||||
sphere(100) {
|
||||
color(Colors.blue)
|
||||
detail = 50
|
||||
opacity = 0.4
|
||||
}
|
||||
repeat(20) {
|
||||
polyline(Point3D(100, 100, 100), Point3D(-100, -100, -100)) {
|
||||
thickness = 208.0
|
||||
rotationX = it * PI2 / 20
|
||||
color(Colors.green)
|
||||
//rotationY = it * PI2 / 20
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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(200)
|
||||
boxes.forEach { box ->
|
||||
box.value = (box.value + Random.nextInt(-15, 15)).coerceIn(0..255)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
override fun dispose() = emptyMap<String, Any>()//mapOf("lines" put presenter.dispose())
|
||||
}
|
||||
|
||||
fun main() {
|
||||
startApplication(::ThreeDemoApp)
|
||||
}
|
Loading…
Reference in New Issue
Block a user