forked from kscience/visionforge
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
|
# DataForge plugins for visualisation
|
||||||
|
|
||||||
## Common visualisation objects
|
## Common visualisation objects
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
|
import scientifik.useSerialization
|
||||||
|
|
||||||
val dataforgeVersion by extra("0.1.5-dev-4")
|
val dataforgeVersion by extra("0.1.5-dev-4")
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
val kotlinVersion = "1.3.61"
|
val kotlinVersion = "1.3.61"
|
||||||
val toolsVersion = "0.2.7"
|
val toolsVersion = "0.3.1"
|
||||||
|
|
||||||
kotlin("jvm") version kotlinVersion apply false
|
kotlin("jvm") version kotlinVersion apply false
|
||||||
id("kotlin-dce-js") version kotlinVersion apply false
|
id("kotlin-dce-js") version kotlinVersion apply false
|
||||||
@ -27,6 +29,10 @@ allprojects {
|
|||||||
version = "0.1.0-dev"
|
version = "0.1.0-dev"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
subprojects{
|
||||||
|
useSerialization("0.13.0")
|
||||||
|
}
|
||||||
|
|
||||||
val githubProject by extra("dataforge-vis")
|
val githubProject by extra("dataforge-vis")
|
||||||
val bintrayRepo by extra("dataforge")
|
val bintrayRepo by extra("dataforge")
|
||||||
|
|
||||||
|
@ -5,10 +5,6 @@ plugins {
|
|||||||
id("org.openjfx.javafxplugin")
|
id("org.openjfx.javafxplugin")
|
||||||
}
|
}
|
||||||
|
|
||||||
scientifik{
|
|
||||||
withSerialization()
|
|
||||||
}
|
|
||||||
|
|
||||||
val dataforgeVersion: String by rootProject.extra
|
val dataforgeVersion: String by rootProject.extra
|
||||||
//val kvisionVersion: String by rootProject.extra("2.0.0-M1")
|
//val kvisionVersion: String by rootProject.extra("2.0.0-M1")
|
||||||
|
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
package hep.dataforge.vis.common
|
package hep.dataforge.vis.common
|
||||||
|
|
||||||
import hep.dataforge.meta.Meta
|
|
||||||
import hep.dataforge.meta.MetaItem
|
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
|
import kotlinx.serialization.Transient
|
||||||
|
|
||||||
|
|
||||||
@ -16,46 +18,7 @@ abstract class AbstractVisualGroup : AbstractVisualObject(), MutableVisualGroup
|
|||||||
/**
|
/**
|
||||||
* A map of top level named children
|
* A map of top level named children
|
||||||
*/
|
*/
|
||||||
abstract override val children: Map<NameToken, VisualObject> //get() = _children
|
abstract override val children: Map<NameToken, VisualObject>
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
override fun propertyChanged(name: Name, before: MetaItem<*>?, after: MetaItem<*>?) {
|
override fun propertyChanged(name: Name, before: MetaItem<*>?, after: MetaItem<*>?) {
|
||||||
super.propertyChanged(name, before, after)
|
super.propertyChanged(name, before, after)
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
package hep.dataforge.vis.common
|
package hep.dataforge.vis.common
|
||||||
|
|
||||||
import hep.dataforge.meta.*
|
import hep.dataforge.meta.*
|
||||||
import hep.dataforge.names.EmptyName
|
|
||||||
import hep.dataforge.names.Name
|
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 hep.dataforge.vis.common.VisualObject.Companion.STYLE_KEY
|
||||||
import kotlinx.serialization.Transient
|
import kotlinx.serialization.Transient
|
||||||
|
|
||||||
@ -17,13 +16,23 @@ abstract class AbstractVisualObject : VisualObject {
|
|||||||
@Transient
|
@Transient
|
||||||
override var parent: VisualObject? = null
|
override var parent: VisualObject? = null
|
||||||
|
|
||||||
abstract override var properties: Config?
|
protected abstract var properties: Config?
|
||||||
|
|
||||||
override var styles: List<Name>
|
override var styles: List<String>
|
||||||
get() = properties?.get(STYLE_KEY).stringList.map(String::toName)
|
get() = properties?.get(STYLE_KEY).stringList
|
||||||
set(value) {
|
set(value) {
|
||||||
setProperty(STYLE_KEY, value.map { it.toString() })
|
//val allStyles = (field + value).distinct()
|
||||||
styleChanged()
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -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<*>? {
|
override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? {
|
||||||
return if (inherit) {
|
return if (inherit) {
|
||||||
properties?.get(name) ?: mergedStyles[name] ?: parent?.getProperty(name, inherit)
|
properties?.get(name) ?: mergedStyles[name] ?: parent?.getProperty(name, inherit)
|
||||||
@ -84,10 +85,10 @@ abstract class AbstractVisualObject : VisualObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun VisualObject.findStyle(styleName: Name): Meta? {
|
//fun VisualObject.findStyle(styleName: Name): Meta? {
|
||||||
if (this is VisualGroup) {
|
// if (this is VisualGroup) {
|
||||||
val style = getStyle(styleName)
|
// val style = resolveStyle(styleName)
|
||||||
if (style != null) return style
|
// if (style != null) return style
|
||||||
}
|
// }
|
||||||
return parent?.findStyle(styleName)
|
// return parent?.findStyle(styleName)
|
||||||
}
|
//}
|
@ -1,5 +1,10 @@
|
|||||||
package hep.dataforge.vis.common
|
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
|
* 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 yellow = 0xFFFF00
|
||||||
const val yellowgreen = 0x9ACD32
|
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 rgbToString(red: UByte, green: UByte, blue: UByte): String {
|
||||||
fun colorToString(color: UByte): String{
|
fun colorToString(color: UByte): String {
|
||||||
return color.toString(16).padStart(2,'0')
|
return color.toString(16).padStart(2, '0')
|
||||||
}
|
}
|
||||||
return buildString {
|
return buildString {
|
||||||
append("#")
|
append("#")
|
||||||
@ -186,4 +220,10 @@ object Colors {
|
|||||||
append(colorToString(blue))
|
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
|
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.names.*
|
||||||
import hep.dataforge.provider.Provider
|
import hep.dataforge.provider.Provider
|
||||||
|
|
||||||
@ -12,6 +14,8 @@ interface VisualGroup : Provider, Iterable<VisualObject>, VisualObject {
|
|||||||
|
|
||||||
override val defaultTarget: String get() = VisualObject.TYPE
|
override val defaultTarget: String get() = VisualObject.TYPE
|
||||||
|
|
||||||
|
val styleSheet: StyleSheet
|
||||||
|
|
||||||
override fun provideTop(target: String): Map<Name, Any> =
|
override fun provideTop(target: String): Map<Name, Any> =
|
||||||
when (target) {
|
when (target) {
|
||||||
VisualObject.TYPE -> children.flatMap { (key, value) ->
|
VisualObject.TYPE -> children.flatMap { (key, value) ->
|
||||||
@ -22,7 +26,7 @@ interface VisualGroup : Provider, Iterable<VisualObject>, VisualObject {
|
|||||||
}
|
}
|
||||||
res.entries
|
res.entries
|
||||||
}.associate { it.toPair() }
|
}.associate { it.toPair() }
|
||||||
//TODO add styles
|
STYLE_TARGET -> styleSheet.items.mapKeys { it.key.toName() }
|
||||||
else -> emptyMap()
|
else -> emptyMap()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,17 +36,6 @@ interface VisualGroup : Provider, Iterable<VisualObject>, VisualObject {
|
|||||||
*/
|
*/
|
||||||
override fun iterator(): Iterator<VisualObject> = children.values.iterator()
|
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? {
|
operator fun get(name: Name): VisualObject? {
|
||||||
return when {
|
return when {
|
||||||
name.isEmpty() -> this
|
name.isEmpty() -> this
|
||||||
@ -50,8 +43,30 @@ interface VisualGroup : Provider, Iterable<VisualObject>, VisualObject {
|
|||||||
else -> (children[name.first()!!] as? VisualGroup)?.get(name.cutFirst())
|
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()
|
val VisualGroup.isEmpty: Boolean get() = this.children.isEmpty()
|
||||||
|
|
||||||
interface MutableVisualGroup : VisualGroup {
|
interface MutableVisualGroup : VisualGroup {
|
@ -1,9 +1,6 @@
|
|||||||
package hep.dataforge.vis.common
|
package hep.dataforge.vis.common
|
||||||
|
|
||||||
import hep.dataforge.meta.Config
|
import hep.dataforge.meta.*
|
||||||
import hep.dataforge.meta.Configurable
|
|
||||||
import hep.dataforge.meta.Laminate
|
|
||||||
import hep.dataforge.meta.MetaItem
|
|
||||||
import hep.dataforge.names.Name
|
import hep.dataforge.names.Name
|
||||||
import hep.dataforge.names.asName
|
import hep.dataforge.names.asName
|
||||||
import hep.dataforge.names.toName
|
import hep.dataforge.names.toName
|
||||||
@ -26,11 +23,6 @@ interface VisualObject : Configurable {
|
|||||||
@Transient
|
@Transient
|
||||||
var parent: VisualObject?
|
var parent: VisualObject?
|
||||||
|
|
||||||
/**
|
|
||||||
* Direct properties access
|
|
||||||
*/
|
|
||||||
val properties: Config?
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set property for this object
|
* Set property for this object
|
||||||
*/
|
*/
|
||||||
@ -42,9 +34,11 @@ interface VisualObject : Configurable {
|
|||||||
fun getProperty(name: Name, inherit: Boolean = true): MetaItem<*>?
|
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
|
* Add listener triggering on property change
|
||||||
@ -57,11 +51,9 @@ interface VisualObject : Configurable {
|
|||||||
fun removeChangeListener(owner: Any?)
|
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>
|
var styles: List<String>
|
||||||
|
|
||||||
fun findAllStyles(): Laminate = Laminate(styles.distinct().mapNotNull(::findStyle))
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val TYPE = "visual"
|
const val TYPE = "visual"
|
||||||
@ -69,12 +61,42 @@ interface VisualObject : Configurable {
|
|||||||
|
|
||||||
//const val META_KEY = "@meta"
|
//const val META_KEY = "@meta"
|
||||||
//const val TAGS_KEY = "@tags"
|
//const val TAGS_KEY = "@tags"
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun VisualObject.getProperty(key: String, inherit: Boolean = true): MetaItem<*>? = getProperty(key.toName(), inherit)
|
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.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 {
|
plugins {
|
||||||
id("scientifik.mpp")
|
id("scientifik.mpp")
|
||||||
}
|
}
|
||||||
|
|
||||||
scientifik{
|
|
||||||
withSerialization()
|
|
||||||
}
|
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
sourceSets {
|
sourceSets {
|
||||||
val commonMain by getting {
|
val commonMain by getting {
|
||||||
dependencies {
|
dependencies {
|
||||||
api(project(":dataforge-vis-spatial"))
|
api(project(":dataforge-vis-spatial"))
|
||||||
api("scientifik:gdml:0.1.3")
|
api("scientifik:gdml:0.1.4")
|
||||||
}
|
|
||||||
}
|
|
||||||
val jsMain by getting {
|
|
||||||
dependencies {
|
|
||||||
api(project(":dataforge-vis-spatial"))
|
|
||||||
//api("kotlin.js.externals:kotlin-js-jquery:3.2.0-0")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks{
|
//tasks{
|
||||||
val jsBrowserWebpack by getting(KotlinWebpack::class) {
|
// val jsBrowserWebpack by getting(KotlinWebpack::class) {
|
||||||
sourceMaps = false
|
// sourceMaps = false
|
||||||
}
|
// }
|
||||||
}
|
//}
|
@ -6,8 +6,8 @@ import hep.dataforge.meta.buildMeta
|
|||||||
import hep.dataforge.names.Name
|
import hep.dataforge.names.Name
|
||||||
import hep.dataforge.names.toName
|
import hep.dataforge.names.toName
|
||||||
import hep.dataforge.vis.common.VisualObject
|
import hep.dataforge.vis.common.VisualObject
|
||||||
import hep.dataforge.vis.common.applyStyle
|
import hep.dataforge.vis.common.useStyle
|
||||||
import hep.dataforge.vis.spatial.Material3D.Companion.COLOR_KEY
|
import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_COLOR_KEY
|
||||||
import hep.dataforge.vis.spatial.RotationOrder
|
import hep.dataforge.vis.spatial.RotationOrder
|
||||||
import hep.dataforge.vis.spatial.VisualGroup3D
|
import hep.dataforge.vis.spatial.VisualGroup3D
|
||||||
import hep.dataforge.vis.spatial.VisualObject3D
|
import hep.dataforge.vis.spatial.VisualObject3D
|
||||||
@ -43,7 +43,7 @@ class GDMLTransformer(val root: GDML) {
|
|||||||
styleCache.getOrPut(name.toName()){
|
styleCache.getOrPut(name.toName()){
|
||||||
buildMeta(builder)
|
buildMeta(builder)
|
||||||
}
|
}
|
||||||
applyStyle(name)
|
useStyle(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun configureSolid(obj: VisualObject3D, parent: GDMLVolume, solid: GDMLSolid) {
|
internal fun configureSolid(obj: VisualObject3D, parent: GDMLVolume, solid: GDMLSolid) {
|
||||||
@ -52,7 +52,7 @@ class GDMLTransformer(val root: GDML) {
|
|||||||
val styleName = "material[${material.name}]"
|
val styleName = "material[${material.name}]"
|
||||||
|
|
||||||
obj.useStyle(styleName){
|
obj.useStyle(styleName){
|
||||||
COLOR_KEY to random.nextInt(0, Int.MAX_VALUE)
|
MATERIAL_COLOR_KEY put random.nextInt(16777216)
|
||||||
"gdml.material" put material.name
|
"gdml.material" put material.name
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ class GDMLTransformer(val root: GDML) {
|
|||||||
internal fun finalize(final: VisualGroup3D): VisualGroup3D {
|
internal fun finalize(final: VisualGroup3D): VisualGroup3D {
|
||||||
final.prototypes = proto
|
final.prototypes = proto
|
||||||
styleCache.forEach {
|
styleCache.forEach {
|
||||||
final.addStyle(it.key, it.value, false)
|
final.styleSheet[it.key.toString()] = it.value
|
||||||
}
|
}
|
||||||
final.rotationOrder = RotationOrder.ZXY
|
final.rotationOrder = RotationOrder.ZXY
|
||||||
onFinish(this@GDMLTransformer)
|
onFinish(this@GDMLTransformer)
|
||||||
|
@ -245,3 +245,10 @@ fun GDML.toVisual(block: GDMLTransformer.() -> Unit = {}): VisualGroup3D {
|
|||||||
|
|
||||||
return context.finalize(volume(context, world))
|
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.objectTree
|
||||||
import hep.dataforge.js.startApplication
|
import hep.dataforge.js.startApplication
|
||||||
import hep.dataforge.names.NameToken
|
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.Visual3DPlugin
|
||||||
import hep.dataforge.vis.spatial.VisualGroup3D
|
import hep.dataforge.vis.spatial.VisualGroup3D
|
||||||
import hep.dataforge.vis.spatial.VisualObject3D
|
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.propertyEditor
|
||||||
import hep.dataforge.vis.spatial.editor.threeOutputConfig
|
import hep.dataforge.vis.spatial.editor.threeOutputConfig
|
||||||
import hep.dataforge.vis.spatial.gdml.GDMLTransformer
|
import hep.dataforge.vis.spatial.gdml.GDMLTransformer
|
||||||
@ -106,7 +105,7 @@ private class GDMLDemoApp : Application {
|
|||||||
|| parent.physVolumes.isNotEmpty()
|
|| parent.physVolumes.isNotEmpty()
|
||||||
) {
|
) {
|
||||||
useStyle("opaque") {
|
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)
|
output.camera.layers.set(0)
|
||||||
configElement.threeOutputConfig(output)
|
configElement.threeOutputConfig(output)
|
||||||
//tree.visualObjectTree(visual, editor::propertyEditor)
|
//tree.visualObjectTree(visual, editor::propertyEditor)
|
||||||
treeElement.objectTree(NameToken("World"),visual, editorElement::propertyEditor)
|
treeElement.objectTree(NameToken("World"), visual, editorElement::propertyEditor)
|
||||||
|
|
||||||
|
|
||||||
output.render(visual)
|
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")
|
id("org.openjfx.javafxplugin")
|
||||||
}
|
}
|
||||||
|
|
||||||
scientifik {
|
|
||||||
withSerialization()
|
|
||||||
}
|
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
jvm {
|
jvm {
|
||||||
withJava()
|
withJava()
|
||||||
@ -21,9 +17,13 @@ kotlin {
|
|||||||
}
|
}
|
||||||
jvmMain {
|
jvmMain {
|
||||||
dependencies {
|
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}")
|
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 {
|
jsMain {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
@file:UseSerializers(Point3DSerializer::class)
|
@file:UseSerializers(Point3DSerializer::class)
|
||||||
|
|
||||||
package hep.dataforge.vis.spatial
|
package hep.dataforge.vis.spatial
|
||||||
|
|
||||||
import hep.dataforge.io.serialization.ConfigSerializer
|
import hep.dataforge.io.serialization.ConfigSerializer
|
||||||
@ -43,10 +44,9 @@ inline fun VisualGroup3D.composite(
|
|||||||
val children = group.filterIsInstance<VisualObject3D>()
|
val children = group.filterIsInstance<VisualObject3D>()
|
||||||
if (children.size != 2) error("Composite requires exactly two children")
|
if (children.size != 2) error("Composite requires exactly two children")
|
||||||
return Composite(type, children[0], children[1]).also {
|
return Composite(type, children[0], children[1]).also {
|
||||||
if (group.properties != null) {
|
|
||||||
it.config.update(group.config)
|
it.config.update(group.config)
|
||||||
it.material = group.material
|
//it.material = group.material
|
||||||
}
|
|
||||||
it.position = group.position
|
it.position = group.position
|
||||||
it.rotation = group.rotation
|
it.rotation = group.rotation
|
||||||
it.scale = group.scale
|
it.scale = group.scale
|
||||||
|
@ -9,7 +9,8 @@ import kotlinx.serialization.Serializable
|
|||||||
import kotlinx.serialization.UseSerializers
|
import kotlinx.serialization.UseSerializers
|
||||||
|
|
||||||
@Serializable
|
@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)
|
@Serializable(ConfigSerializer::class)
|
||||||
override var properties: Config? = null
|
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 = {}) =
|
fun VisualGroup3D.label(
|
||||||
Text3D(text, fontSize).apply(action).also { set(name, it) }
|
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.meta.*
|
||||||
import hep.dataforge.names.asName
|
import hep.dataforge.names.asName
|
||||||
import hep.dataforge.names.plus
|
import hep.dataforge.names.plus
|
||||||
import hep.dataforge.vis.common.VisualObject
|
import hep.dataforge.vis.common.Colors
|
||||||
import hep.dataforge.vis.spatial.Material3D.Companion.COLOR_KEY
|
import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_COLOR_KEY
|
||||||
import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_KEY
|
import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_OPACITY_KEY
|
||||||
import hep.dataforge.vis.spatial.Material3D.Companion.OPACITY_KEY
|
|
||||||
|
|
||||||
class Material3D(override val config: Config) : Specific {
|
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> {
|
companion object : Specification<Material3D> {
|
||||||
override fun wrap(config: Config): Material3D = Material3D(config)
|
override fun wrap(config: Config): Material3D = Material3D(config)
|
||||||
|
|
||||||
val MATERIAL_KEY = "material".asName()
|
val MATERIAL_KEY = "material".asName()
|
||||||
val COLOR_KEY = MATERIAL_KEY + "color"
|
internal val COLOR_KEY = "color".asName()
|
||||||
val SPECULAR_COLOR = MATERIAL_KEY + "specularColor"
|
val MATERIAL_COLOR_KEY = MATERIAL_KEY + COLOR_KEY
|
||||||
val OPACITY_KEY = MATERIAL_KEY + "opacity"
|
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) {
|
fun VisualObject3D.color(rgb: String) {
|
||||||
setProperty(COLOR_KEY, rgb)
|
setProperty(MATERIAL_COLOR_KEY, rgb)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun VisualObject.color(rgb: Int) {
|
fun VisualObject3D.color(rgb: Int) {
|
||||||
setProperty(COLOR_KEY, rgb)
|
setProperty(MATERIAL_COLOR_KEY, rgb)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun VisualObject.color(r: UByte, g: UByte, b: UByte) = setProperty(
|
fun VisualObject3D.color(r: UByte, g: UByte, b: UByte) = setProperty(
|
||||||
COLOR_KEY,
|
MATERIAL_COLOR_KEY,
|
||||||
buildMeta {
|
Colors.rgbToMeta(r, g, b)
|
||||||
"red" put r.toInt()
|
|
||||||
"green" put g.toInt()
|
|
||||||
"blue" put b.toInt()
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
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) {
|
set(value) {
|
||||||
if (value != null) {
|
setProperty(MATERIAL_COLOR_KEY, value)
|
||||||
color(value)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var VisualObject.material: Material3D?
|
//var VisualObject3D.material: Material3D?
|
||||||
get() = getProperty(MATERIAL_KEY).node?.let { Material3D.wrap(it) }
|
// get() = getProperty(MATERIAL_KEY).node?.let { Material3D.wrap(it) }
|
||||||
set(value) = setProperty(MATERIAL_KEY, value?.config)
|
// set(value) = setProperty(MATERIAL_KEY, value?.config)
|
||||||
|
|
||||||
fun VisualObject.material(builder: Material3D.() -> Unit) {
|
fun VisualObject3D.material(builder: Material3D.() -> Unit) {
|
||||||
material = Material3D.build(builder)
|
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?
|
var VisualObject3D.opacity: Double?
|
||||||
get() = getProperty(OPACITY_KEY).double
|
get() = getProperty(MATERIAL_OPACITY_KEY).double
|
||||||
set(value) {
|
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.ConfigSerializer
|
||||||
import hep.dataforge.io.serialization.NameSerializer
|
import hep.dataforge.io.serialization.NameSerializer
|
||||||
import hep.dataforge.meta.Config
|
import hep.dataforge.meta.Config
|
||||||
import hep.dataforge.meta.Meta
|
|
||||||
import hep.dataforge.meta.MetaItem
|
import hep.dataforge.meta.MetaItem
|
||||||
import hep.dataforge.meta.get
|
import hep.dataforge.meta.get
|
||||||
import hep.dataforge.names.Name
|
import hep.dataforge.names.Name
|
||||||
import hep.dataforge.names.NameToken
|
import hep.dataforge.names.NameToken
|
||||||
import hep.dataforge.names.asName
|
import hep.dataforge.names.asName
|
||||||
import hep.dataforge.names.plus
|
import hep.dataforge.names.plus
|
||||||
import hep.dataforge.vis.common.AbstractVisualObject
|
import hep.dataforge.vis.common.*
|
||||||
import hep.dataforge.vis.common.MutableVisualGroup
|
|
||||||
import hep.dataforge.vis.common.VisualGroup
|
|
||||||
import hep.dataforge.vis.common.VisualObject
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.Transient
|
import kotlinx.serialization.Transient
|
||||||
import kotlinx.serialization.UseSerializers
|
import kotlinx.serialization.UseSerializers
|
||||||
@ -43,12 +39,8 @@ class Proxy(val templateName: Name) : AbstractVisualObject(), VisualGroup, Visua
|
|||||||
get() = (parent as? VisualGroup3D)?.getPrototype(templateName)
|
get() = (parent as? VisualGroup3D)?.getPrototype(templateName)
|
||||||
?: error("Template with name $templateName not found in $parent")
|
?: error("Template with name $templateName not found in $parent")
|
||||||
|
|
||||||
override fun getStyle(name: Name): Meta? = (parent as VisualGroup?)?.getStyle(name)
|
override val styleSheet: StyleSheet
|
||||||
|
get() = (parent as? VisualGroup)?.styleSheet ?: StyleSheet(this)
|
||||||
override fun addStyle(name: Name, meta: Meta, apply: Boolean) {
|
|
||||||
(parent as VisualGroup?)?.addStyle(name, meta, apply)
|
|
||||||
//do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? {
|
override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? {
|
||||||
return if (inherit) {
|
return if (inherit) {
|
||||||
@ -77,16 +69,17 @@ class Proxy(val templateName: Name) : AbstractVisualObject(), VisualGroup, Visua
|
|||||||
return NameToken(PROXY_CHILD_PROPERTY_PREFIX, childName.toString()) + propertyName
|
return NameToken(PROXY_CHILD_PROPERTY_PREFIX, childName.toString()) + propertyName
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun prototypeFor(name: Name): VisualObject =
|
private fun prototypeFor(name: Name): VisualObject {
|
||||||
(prototype as? VisualGroup)?.get(name)
|
return (prototype as? VisualGroup)?.get(name)
|
||||||
?: error("Prototype with name $name not found in ${this@Proxy}")
|
?: error("Prototype with name $name not found in $this")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
override var styles: List<Name>
|
override var styles: List<String>
|
||||||
get() = super.styles + prototype.styles
|
get() = super.styles + prototype.styles
|
||||||
set(value) {
|
set(value) {
|
||||||
setProperty(VisualObject.STYLE_KEY, value.map { it.toString() })
|
setProperty(VisualObject.STYLE_KEY, value)
|
||||||
styleChanged()
|
updateStyles(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
//override fun findAllStyles(): Laminate = Laminate((styles + prototype.styles).mapNotNull { findStyle(it) })
|
//override fun findAllStyles(): Laminate = Laminate((styles + prototype.styles).mapNotNull { findStyle(it) })
|
||||||
@ -94,9 +87,9 @@ class Proxy(val templateName: Name) : AbstractVisualObject(), VisualGroup, Visua
|
|||||||
@Serializable
|
@Serializable
|
||||||
inner class ProxyChild(val name: Name) : AbstractVisualObject(), VisualGroup {
|
inner class ProxyChild(val name: Name) : AbstractVisualObject(), VisualGroup {
|
||||||
|
|
||||||
val prototype: VisualObject by lazy {
|
val prototype: VisualObject get() = prototypeFor(name)
|
||||||
prototypeFor(name)
|
|
||||||
}
|
override val styleSheet: StyleSheet get() = this@Proxy.styleSheet
|
||||||
|
|
||||||
override val children: Map<NameToken, VisualObject>
|
override val children: Map<NameToken, VisualObject>
|
||||||
get() = (prototype as? VisualGroup)?.children?.mapValues { (key, _) ->
|
get() = (prototype as? VisualGroup)?.children?.mapValues { (key, _) ->
|
||||||
@ -105,12 +98,6 @@ class Proxy(val templateName: Name) : AbstractVisualObject(), VisualGroup, Visua
|
|||||||
)
|
)
|
||||||
} ?: emptyMap()
|
} ?: 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?
|
override var properties: Config?
|
||||||
get() = propertyCache[name]
|
get() = propertyCache[name]
|
||||||
set(value) {
|
set(value) {
|
||||||
@ -176,8 +163,8 @@ fun VisualGroup3D.proxy(
|
|||||||
): Proxy {
|
): Proxy {
|
||||||
val existing = getPrototype(templateName)
|
val existing = getPrototype(templateName)
|
||||||
if (existing == null) {
|
if (existing == null) {
|
||||||
setPrototype(templateName,obj, attachToParent)
|
setPrototype(templateName, obj, attachToParent)
|
||||||
} else if(existing != obj) {
|
} else if (existing != obj) {
|
||||||
error("Can't add different prototype on top of existing one")
|
error("Can't add different prototype on top of existing one")
|
||||||
}
|
}
|
||||||
return ref(templateName, name, block)
|
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.MetaSerializer
|
||||||
import hep.dataforge.io.serialization.NameSerializer
|
import hep.dataforge.io.serialization.NameSerializer
|
||||||
import hep.dataforge.meta.Config
|
import hep.dataforge.meta.Config
|
||||||
import hep.dataforge.meta.Meta
|
|
||||||
import hep.dataforge.names.Name
|
import hep.dataforge.names.Name
|
||||||
import hep.dataforge.names.NameToken
|
import hep.dataforge.names.NameToken
|
||||||
import hep.dataforge.names.asName
|
import hep.dataforge.names.asName
|
||||||
import hep.dataforge.names.isEmpty
|
import hep.dataforge.names.isEmpty
|
||||||
import hep.dataforge.vis.common.AbstractVisualGroup
|
import hep.dataforge.vis.common.AbstractVisualGroup
|
||||||
|
import hep.dataforge.vis.common.StyleSheet
|
||||||
import hep.dataforge.vis.common.VisualGroup
|
import hep.dataforge.vis.common.VisualGroup
|
||||||
import hep.dataforge.vis.common.VisualObject
|
import hep.dataforge.vis.common.VisualObject
|
||||||
import kotlinx.serialization.SerialName
|
import kotlinx.serialization.SerialName
|
||||||
@ -37,9 +37,10 @@ class VisualGroup3D : AbstractVisualGroup(), VisualObject3D {
|
|||||||
field = value
|
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
|
//FIXME to be lifted to AbstractVisualGroup after https://github.com/Kotlin/kotlinx.serialization/issues/378 is fixed
|
||||||
override var properties: Config? = null
|
override var properties: Config? = null
|
||||||
override val styleSheet = HashMap<Name, Meta>()
|
|
||||||
|
|
||||||
override var position: Point3D? = null
|
override var position: Point3D? = null
|
||||||
override var rotation: Point3D? = null
|
override var rotation: Point3D? = null
|
||||||
@ -49,6 +50,11 @@ class VisualGroup3D : AbstractVisualGroup(), VisualObject3D {
|
|||||||
private val _children = HashMap<NameToken, VisualObject>()
|
private val _children = HashMap<NameToken, VisualObject>()
|
||||||
override val children: Map<NameToken, VisualObject> get() = _children
|
override val children: Map<NameToken, VisualObject> get() = _children
|
||||||
|
|
||||||
|
init {
|
||||||
|
//Do after deserialization
|
||||||
|
attachChildren()
|
||||||
|
}
|
||||||
|
|
||||||
override fun removeChild(token: NameToken) {
|
override fun removeChild(token: NameToken) {
|
||||||
_children.remove(token)
|
_children.remove(token)
|
||||||
childrenChanged(token.asName(), null)
|
childrenChanged(token.asName(), null)
|
||||||
@ -96,26 +102,18 @@ class VisualGroup3D : AbstractVisualGroup(), VisualObject3D {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun attachChildren() {
|
||||||
|
super.attachChildren()
|
||||||
|
prototypes?.run {
|
||||||
|
parent = this
|
||||||
|
attachChildren()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val PROTOTYPES_KEY = "templates"
|
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 =
|
fun VisualGroup3D.group(key: String = "", action: VisualGroup3D.() -> Unit = {}): VisualGroup3D =
|
||||||
VisualGroup3D().apply(action).also { set(key, it) }
|
VisualGroup3D().apply(action).also { set(key, it) }
|
@ -129,21 +129,21 @@ var VisualObject3D.x: Number
|
|||||||
get() = position?.x ?: 0f
|
get() = position?.x ?: 0f
|
||||||
set(value) {
|
set(value) {
|
||||||
position().x = value.toDouble()
|
position().x = value.toDouble()
|
||||||
propertyChanged(VisualObject3D.xPos)
|
propertyInvalidated(VisualObject3D.xPos)
|
||||||
}
|
}
|
||||||
|
|
||||||
var VisualObject3D.y: Number
|
var VisualObject3D.y: Number
|
||||||
get() = position?.y ?: 0f
|
get() = position?.y ?: 0f
|
||||||
set(value) {
|
set(value) {
|
||||||
position().y = value.toDouble()
|
position().y = value.toDouble()
|
||||||
propertyChanged(VisualObject3D.yPos)
|
propertyInvalidated(VisualObject3D.yPos)
|
||||||
}
|
}
|
||||||
|
|
||||||
var VisualObject3D.z: Number
|
var VisualObject3D.z: Number
|
||||||
get() = position?.z ?: 0f
|
get() = position?.z ?: 0f
|
||||||
set(value) {
|
set(value) {
|
||||||
position().z = value.toDouble()
|
position().z = value.toDouble()
|
||||||
propertyChanged(VisualObject3D.zPos)
|
propertyInvalidated(VisualObject3D.zPos)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun VisualObject3D.rotation(): Point3D =
|
private fun VisualObject3D.rotation(): Point3D =
|
||||||
@ -153,21 +153,21 @@ var VisualObject3D.rotationX: Number
|
|||||||
get() = rotation?.x ?: 0f
|
get() = rotation?.x ?: 0f
|
||||||
set(value) {
|
set(value) {
|
||||||
rotation().x = value.toDouble()
|
rotation().x = value.toDouble()
|
||||||
propertyChanged(VisualObject3D.xRotation)
|
propertyInvalidated(VisualObject3D.xRotation)
|
||||||
}
|
}
|
||||||
|
|
||||||
var VisualObject3D.rotationY: Number
|
var VisualObject3D.rotationY: Number
|
||||||
get() = rotation?.y ?: 0f
|
get() = rotation?.y ?: 0f
|
||||||
set(value) {
|
set(value) {
|
||||||
rotation().y = value.toDouble()
|
rotation().y = value.toDouble()
|
||||||
propertyChanged(VisualObject3D.yRotation)
|
propertyInvalidated(VisualObject3D.yRotation)
|
||||||
}
|
}
|
||||||
|
|
||||||
var VisualObject3D.rotationZ: Number
|
var VisualObject3D.rotationZ: Number
|
||||||
get() = rotation?.z ?: 0f
|
get() = rotation?.z ?: 0f
|
||||||
set(value) {
|
set(value) {
|
||||||
rotation().z = value.toDouble()
|
rotation().z = value.toDouble()
|
||||||
propertyChanged(VisualObject3D.zRotation)
|
propertyInvalidated(VisualObject3D.zRotation)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun VisualObject3D.scale(): Point3D =
|
private fun VisualObject3D.scale(): Point3D =
|
||||||
@ -177,19 +177,19 @@ var VisualObject3D.scaleX: Number
|
|||||||
get() = scale?.x ?: 1f
|
get() = scale?.x ?: 1f
|
||||||
set(value) {
|
set(value) {
|
||||||
scale().x = value.toDouble()
|
scale().x = value.toDouble()
|
||||||
propertyChanged(VisualObject3D.xScale)
|
propertyInvalidated(VisualObject3D.xScale)
|
||||||
}
|
}
|
||||||
|
|
||||||
var VisualObject3D.scaleY: Number
|
var VisualObject3D.scaleY: Number
|
||||||
get() = scale?.y ?: 1f
|
get() = scale?.y ?: 1f
|
||||||
set(value) {
|
set(value) {
|
||||||
scale().y = value.toDouble()
|
scale().y = value.toDouble()
|
||||||
propertyChanged(VisualObject3D.yScale)
|
propertyInvalidated(VisualObject3D.yScale)
|
||||||
}
|
}
|
||||||
|
|
||||||
var VisualObject3D.scaleZ: Number
|
var VisualObject3D.scaleZ: Number
|
||||||
get() = scale?.z ?: 1f
|
get() = scale?.z ?: 1f
|
||||||
set(value) {
|
set(value) {
|
||||||
scale().z = value.toDouble()
|
scale().z = value.toDouble()
|
||||||
propertyChanged(VisualObject3D.zScale)
|
propertyInvalidated(VisualObject3D.zScale)
|
||||||
}
|
}
|
@ -14,8 +14,8 @@ operator fun Point2D.component1() = x
|
|||||||
operator fun Point2D.component2() = y
|
operator fun Point2D.component2() = y
|
||||||
|
|
||||||
fun Point2D.toMeta() = buildMeta {
|
fun Point2D.toMeta() = buildMeta {
|
||||||
VisualObject3D.x to x
|
VisualObject3D.x put x
|
||||||
VisualObject3D.y to y
|
VisualObject3D.y put y
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Meta.point2D() = Point2D(this["x"].number ?: 0, this["y"].number ?: 0)
|
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)
|
val zero = Point3D(0, 0, 0)
|
||||||
|
|
||||||
fun Point3D.toMeta() = buildMeta {
|
fun Point3D.toMeta() = buildMeta {
|
||||||
VisualObject3D.x to x
|
VisualObject3D.x put x
|
||||||
VisualObject3D.y to y
|
VisualObject3D.y put y
|
||||||
VisualObject3D.z to z
|
VisualObject3D.z put z
|
||||||
}
|
}
|
@ -10,7 +10,9 @@ import hep.dataforge.vis.spatial.*
|
|||||||
internal fun mergeChild(parent: VisualGroup, child: VisualObject): VisualObject {
|
internal fun mergeChild(parent: VisualGroup, child: VisualObject): VisualObject {
|
||||||
return child.apply {
|
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) {
|
if (this is VisualObject3D && parent is VisualObject3D) {
|
||||||
position += parent.position
|
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.meta.*
|
||||||
import hep.dataforge.vis.common.VisualObject
|
import hep.dataforge.vis.common.VisualObject
|
||||||
import hep.dataforge.vis.common.findStyle
|
import hep.dataforge.vis.common.findStyle
|
||||||
import hep.dataforge.vis.spatial.Material3D.Companion.COLOR_KEY
|
import hep.dataforge.vis.spatial.*
|
||||||
import hep.dataforge.vis.spatial.Material3D.Companion.OPACITY_KEY
|
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.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.dom.append
|
||||||
import kotlinx.html.js.div
|
import kotlinx.html.js.div
|
||||||
import kotlinx.html.js.h4
|
import kotlinx.html.js.h4
|
||||||
@ -27,11 +24,17 @@ fun Element.propertyEditor(item: VisualObject?) {
|
|||||||
if (item != null) {
|
if (item != null) {
|
||||||
append {
|
append {
|
||||||
card("Properties") {
|
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 {
|
val metaToEdit = config.builder().apply {
|
||||||
VISIBLE_KEY to (item.visible ?: true)
|
VISIBLE_KEY to (item.visible ?: true)
|
||||||
COLOR_KEY to (item.color ?: "#ffffff")
|
if (item is VisualObject3D) {
|
||||||
OPACITY_KEY to (item.opacity ?: 1.0)
|
MATERIAL_COLOR_KEY to (item.color ?: "#ffffff")
|
||||||
|
MATERIAL_OPACITY_KEY to (item.opacity ?: 1.0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val dMeta: dynamic = metaToEdit.toDynamic()
|
val dMeta: dynamic = metaToEdit.toDynamic()
|
||||||
val options: JSONEditorOptions = jsObject {
|
val options: JSONEditorOptions = jsObject {
|
||||||
|
@ -35,7 +35,7 @@ abstract class MeshThreeFactory<in T : VisualObject3D>(
|
|||||||
//JS sometimes tries to pass Geometry as BufferGeometry
|
//JS sometimes tries to pass Geometry as BufferGeometry
|
||||||
@Suppress("USELESS_IS_CHECK") if (geometry !is BufferGeometry) error("BufferGeometry expected")
|
@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 {
|
val mesh = Mesh(geometry, MeshBasicMaterial()).apply {
|
||||||
matrixAutoUpdate = false
|
matrixAutoUpdate = false
|
||||||
|
@ -11,7 +11,6 @@ import info.laht.threekt.materials.MeshBasicMaterial
|
|||||||
import info.laht.threekt.materials.MeshPhongMaterial
|
import info.laht.threekt.materials.MeshPhongMaterial
|
||||||
import info.laht.threekt.math.Color
|
import info.laht.threekt.math.Color
|
||||||
import info.laht.threekt.objects.Mesh
|
import info.laht.threekt.objects.Mesh
|
||||||
import kotlin.math.max
|
|
||||||
|
|
||||||
|
|
||||||
object ThreeMaterials {
|
object ThreeMaterials {
|
||||||
@ -46,12 +45,6 @@ object ThreeMaterials {
|
|||||||
linewidth = meta["thickness"].double ?: 1.0
|
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 -> {
|
is MetaItem.NodeItem -> {
|
||||||
Color(
|
Color(
|
||||||
node["red"]?.int ?: 0,
|
node[Colors.RED_KEY]?.int ?: 0,
|
||||||
node["green"]?.int ?: 0,
|
node[Colors.GREEN_KEY]?.int ?: 0,
|
||||||
node["blue"]?.int ?: 0
|
node[Colors.BLUE_KEY]?.int ?: 0
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -99,11 +92,12 @@ fun MetaItem<*>.color(): Color {
|
|||||||
//fun Material3D?.jsLineMaterial(): Material = this?.config.jsLineMaterial()
|
//fun Material3D?.jsLineMaterial(): Material = this?.config.jsLineMaterial()
|
||||||
|
|
||||||
fun Mesh.updateMaterial(obj: VisualObject) {
|
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 {
|
material = (material as? MeshBasicMaterial ?: MeshBasicMaterial()).apply {
|
||||||
color = meta["color"]?.color() ?: ThreeMaterials.DEFAULT_COLOR
|
color = meta[Material3D.COLOR_KEY]?.color() ?: ThreeMaterials.DEFAULT_COLOR
|
||||||
opacity = meta["opacity"]?.double ?: 1.0
|
opacity = meta[Material3D.OPACITY_KEY]?.double ?: 1.0
|
||||||
transparent = meta["transparent"].boolean ?: (opacity < 1.0)
|
transparent = opacity < 1.0
|
||||||
|
wireframe = meta[Material3D.WIREFRAME_KEY].boolean?:false
|
||||||
needsUpdate = true
|
needsUpdate = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ class ThreePlugin : AbstractPlugin() {
|
|||||||
objectFactories[Sphere::class] = ThreeSphereFactory
|
objectFactories[Sphere::class] = ThreeSphereFactory
|
||||||
objectFactories[ConeSegment::class] = ThreeCylinderFactory
|
objectFactories[ConeSegment::class] = ThreeCylinderFactory
|
||||||
objectFactories[PolyLine::class] = ThreeLineFactory
|
objectFactories[PolyLine::class] = ThreeLineFactory
|
||||||
|
objectFactories[Label3D::class] = ThreeTextFactory
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@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.context.*
|
||||||
import hep.dataforge.meta.Meta
|
import hep.dataforge.meta.Meta
|
||||||
|
import hep.dataforge.meta.boolean
|
||||||
import hep.dataforge.meta.get
|
import hep.dataforge.meta.get
|
||||||
import hep.dataforge.provider.Type
|
import hep.dataforge.provider.Type
|
||||||
import hep.dataforge.vis.spatial.*
|
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 hep.dataforge.vis.spatial.fx.FX3DFactory.Companion.TYPE
|
||||||
import javafx.scene.Group
|
import javafx.scene.Group
|
||||||
import javafx.scene.Node
|
import javafx.scene.Node
|
||||||
|
import javafx.scene.shape.CullFace
|
||||||
|
import javafx.scene.shape.DrawMode
|
||||||
import javafx.scene.shape.Shape3D
|
import javafx.scene.shape.Shape3D
|
||||||
|
import javafx.scene.text.Font
|
||||||
|
import javafx.scene.text.Text
|
||||||
import javafx.scene.transform.Rotate
|
import javafx.scene.transform.Rotate
|
||||||
import org.fxyz3d.shapes.composites.PolyLine3D
|
import org.fxyz3d.shapes.composites.PolyLine3D
|
||||||
import org.fxyz3d.shapes.primitives.CuboidMesh
|
import org.fxyz3d.shapes.primitives.CuboidMesh
|
||||||
@ -56,11 +63,18 @@ class FX3DPlugin : AbstractPlugin() {
|
|||||||
} else {
|
} else {
|
||||||
FXShapeFactory(obj, binding)
|
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(
|
is PolyLine -> PolyLine3D(
|
||||||
obj.points.map { it.point },
|
obj.points.map { it.point },
|
||||||
obj.thickness.toFloat(),
|
obj.thickness.toFloat(),
|
||||||
obj.material?.get("color")?.color()
|
obj.getProperty(Material3D.MATERIAL_COLOR_KEY)?.color()
|
||||||
)
|
).apply {
|
||||||
|
this.meshView.cullFace = CullFace.FRONT
|
||||||
|
}
|
||||||
else -> {
|
else -> {
|
||||||
//find specialized factory for this type if it is present
|
//find specialized factory for this type if it is present
|
||||||
val factory: FX3DFactory<VisualObject3D>? = findObjectFactory(obj::class)
|
val factory: FX3DFactory<VisualObject3D>? = findObjectFactory(obj::class)
|
||||||
@ -79,15 +93,15 @@ class FX3DPlugin : AbstractPlugin() {
|
|||||||
scaleZProperty().bind(binding[VisualObject3D.zScale].float(obj.scaleZ.toFloat()))
|
scaleZProperty().bind(binding[VisualObject3D.zScale].float(obj.scaleZ.toFloat()))
|
||||||
|
|
||||||
val rotateX = Rotate(0.0, Rotate.X_AXIS).apply {
|
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 {
|
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 {
|
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) {
|
when (obj.rotationOrder) {
|
||||||
@ -100,9 +114,17 @@ class FX3DPlugin : AbstractPlugin() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this is Shape3D) {
|
if (this is Shape3D) {
|
||||||
materialProperty().bind(binding[Material3D.MATERIAL_KEY].transform {
|
materialProperty().bind(binding[MATERIAL_KEY].transform {
|
||||||
it.material()
|
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.setRadius(meta["axis.width"].double ?: LINE_WIDTH)
|
||||||
it.isVisible = meta["axis.visible"].boolean ?: (meta["axis"] != null)
|
it.isVisible = meta["axis.visible"].boolean ?: (meta["axis"] != null)
|
||||||
world.add(it)
|
world.add(it)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val light = AmbientLight()
|
||||||
|
|
||||||
private val camera = PerspectiveCamera().apply {
|
private val camera = PerspectiveCamera().apply {
|
||||||
nearClip = CAMERA_NEAR_CLIP
|
nearClip = CAMERA_NEAR_CLIP
|
||||||
farClip = CAMERA_FAR_CLIP
|
farClip = CAMERA_FAR_CLIP
|
||||||
translateZ = CAMERA_INITIAL_DISTANCE
|
translateZ = CAMERA_INITIAL_DISTANCE
|
||||||
|
this.add(light)
|
||||||
}
|
}
|
||||||
|
|
||||||
val cameraTransform = CameraTransformer().also {
|
val cameraTransform = CameraTransformer().also {
|
||||||
@ -60,20 +62,27 @@ class FXCanvas3D(val plugin: FX3DPlugin, meta: Meta = EmptyMeta) :
|
|||||||
val rotationZProperty get() = cameraTransform.rz.angleProperty()
|
val rotationZProperty get() = cameraTransform.rz.angleProperty()
|
||||||
var angleZ by rotationZProperty
|
var angleZ by rotationZProperty
|
||||||
|
|
||||||
override val root = borderpane {
|
private val canvas = SubScene(
|
||||||
center = SubScene(
|
|
||||||
Group(world, cameraTransform).apply { DepthTest.ENABLE },
|
Group(world, cameraTransform).apply { DepthTest.ENABLE },
|
||||||
1024.0,
|
400.0,
|
||||||
768.0,
|
400.0,
|
||||||
true,
|
true,
|
||||||
SceneAntialiasing.BALANCED
|
SceneAntialiasing.BALANCED
|
||||||
).also { scene ->
|
).also { scene ->
|
||||||
scene.fill = Color.GREY
|
scene.fill = Color.GREY
|
||||||
scene.camera = camera
|
scene.camera = camera
|
||||||
id = "canvas"
|
//id = "canvas"
|
||||||
handleKeyboard(scene)
|
handleKeyboard(scene)
|
||||||
handleMouse(scene)
|
handleMouse(scene)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val root = borderpane {
|
||||||
|
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 ROTATION_SPEED = 2.0
|
||||||
private const val TRACK_SPEED = 6.0
|
private const val TRACK_SPEED = 6.0
|
||||||
private const val RESIZE_SPEED = 50.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
|
package hep.dataforge.vis.spatial.fx
|
||||||
|
|
||||||
import eu.mihosoft.jcsg.CSG
|
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.Composite
|
||||||
import hep.dataforge.vis.spatial.CompositeType
|
import hep.dataforge.vis.spatial.CompositeType
|
||||||
import javafx.scene.Group
|
import javafx.scene.Group
|
||||||
import javafx.scene.Node
|
import javafx.scene.Node
|
||||||
import javafx.scene.shape.MeshView
|
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
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
class FXCompositeFactory(val plugin: FX3DPlugin) :
|
private fun MeshView.toCSG(): CSG {
|
||||||
FX3DFactory<Composite> {
|
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>
|
override val type: KClass<in Composite>
|
||||||
get() = Composite::class
|
get() = Composite::class
|
||||||
|
|
||||||
override fun invoke(obj: Composite, binding: VisualObjectFXBinding): Node {
|
override fun invoke(obj: Composite, binding: VisualObjectFXBinding): Node {
|
||||||
val first = plugin.buildNode(obj.first) as? MeshView ?: error("Can't build 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 second = plugin.buildNode(obj.second) as? MeshView ?: error("Can't build node")
|
||||||
val firstCSG = MeshUtils.mesh2CSG(first)
|
val firstCSG = first.toCSG()
|
||||||
val secondCSG = MeshUtils.mesh2CSG(second)
|
val secondCSG = second.toCSG()
|
||||||
val resultCSG = when(obj.compositeType){
|
val resultCSG = when (obj.compositeType) {
|
||||||
CompositeType.UNION -> firstCSG.union(secondCSG)
|
CompositeType.UNION -> firstCSG.union(secondCSG)
|
||||||
CompositeType.INTERSECT -> firstCSG.intersect(secondCSG)
|
CompositeType.INTERSECT -> firstCSG.intersect(secondCSG)
|
||||||
CompositeType.SUBTRACT -> firstCSG.difference(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
|
val meshes = toJavaFXMesh().asMeshViews
|
||||||
return if(meshes.size == 1){
|
return if (meshes.size == 1) {
|
||||||
meshes.first()
|
meshes.first()
|
||||||
} else {
|
} else {
|
||||||
Group(meshes.map { it })
|
Group(meshes.map { it })
|
||||||
|
@ -5,6 +5,8 @@ import hep.dataforge.meta.double
|
|||||||
import hep.dataforge.meta.get
|
import hep.dataforge.meta.get
|
||||||
import hep.dataforge.meta.int
|
import hep.dataforge.meta.int
|
||||||
import hep.dataforge.values.ValueType
|
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.Color
|
||||||
import javafx.scene.paint.Material
|
import javafx.scene.paint.Material
|
||||||
import javafx.scene.paint.PhongMaterial
|
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 red = int and 0x00ff0000 shr 16
|
||||||
val green = int and 0x0000ff00 shr 8
|
val green = int and 0x0000ff00 shr 8
|
||||||
val blue = int and 0x000000ff
|
val blue = int and 0x000000ff
|
||||||
Color.rgb(red, green, blue)
|
Color.rgb(red, green, blue, opacity)
|
||||||
} else {
|
} else {
|
||||||
Color.web(this.value.string)
|
Color.web(this.value.string)
|
||||||
}
|
}
|
||||||
is MetaItem.NodeItem -> {
|
is MetaItem.NodeItem -> {
|
||||||
Color.rgb(
|
Color.rgb(
|
||||||
node["red"]?.int ?: 0,
|
node[Colors.RED_KEY]?.int ?: 0,
|
||||||
node["green"]?.int ?: 0,
|
node[Colors.GREEN_KEY]?.int ?: 0,
|
||||||
node["blue"]?.int ?: 0,
|
node[Colors.BLUE_KEY]?.int ?: 0,
|
||||||
node["opacity"]?.double ?: opacity
|
node[Material3D.OPACITY_KEY]?.double ?: opacity
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -62,9 +64,9 @@ fun MetaItem<*>?.material(): Material {
|
|||||||
null -> FXMaterials.GREY
|
null -> FXMaterials.GREY
|
||||||
is MetaItem.ValueItem -> PhongMaterial(color())
|
is MetaItem.ValueItem -> PhongMaterial(color())
|
||||||
is MetaItem.NodeItem -> PhongMaterial().apply {
|
is MetaItem.NodeItem -> PhongMaterial().apply {
|
||||||
val opacity = node["opacity"].double ?: 1.0
|
val opacity = node[Material3D.OPACITY_KEY].double ?: 1.0
|
||||||
diffuseColor = node["color"]?.color(opacity) ?: Color.DARKGREY
|
diffuseColor = node[Material3D.COLOR_KEY]?.color(opacity) ?: Color.DARKGREY
|
||||||
specularColor = node["specularColor"]?.color(opacity) ?: Color.WHITE
|
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.Mesh
|
||||||
import javafx.scene.shape.MeshView
|
import javafx.scene.shape.MeshView
|
||||||
import javafx.scene.shape.TriangleMesh
|
import javafx.scene.shape.TriangleMesh
|
||||||
|
import javafx.scene.shape.VertexFormat
|
||||||
import org.fxyz3d.geometry.Face3
|
import org.fxyz3d.geometry.Face3
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
@ -42,13 +43,16 @@ private class FXGeometryBuilder : GeometryBuilder<Mesh> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun build(): Mesh {
|
override fun build(): Mesh {
|
||||||
val mesh = TriangleMesh()
|
val mesh = TriangleMesh(VertexFormat.POINT_TEXCOORD)
|
||||||
vertices.forEach {
|
vertices.forEach {
|
||||||
//TODO optimize copy
|
//TODO optimize copy
|
||||||
mesh.points.addAll(it.x.toFloat(), it.y.toFloat(), it.z.toFloat())
|
mesh.points.addAll(it.x.toFloat(), it.y.toFloat(), it.z.toFloat())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mesh.texCoords.addAll(0f, 0f)
|
||||||
|
|
||||||
faces.forEach {
|
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
|
return mesh
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,10 @@ package hep.dataforge.vis.spatial.fx
|
|||||||
|
|
||||||
import hep.dataforge.meta.*
|
import hep.dataforge.meta.*
|
||||||
import hep.dataforge.names.Name
|
import hep.dataforge.names.Name
|
||||||
import hep.dataforge.names.isEmpty
|
import hep.dataforge.names.startsWith
|
||||||
import hep.dataforge.names.toName
|
import hep.dataforge.names.toName
|
||||||
import hep.dataforge.vis.common.VisualObject
|
import hep.dataforge.vis.common.VisualObject
|
||||||
|
import javafx.application.Platform
|
||||||
import javafx.beans.binding.ObjectBinding
|
import javafx.beans.binding.ObjectBinding
|
||||||
import tornadofx.*
|
import tornadofx.*
|
||||||
|
|
||||||
@ -16,13 +17,18 @@ class VisualObjectFXBinding(val obj: VisualObject) {
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
obj.onPropertyChange(this) { name, _, _ ->
|
obj.onPropertyChange(this) { name, _, _ ->
|
||||||
var currentName = name
|
bindings.filter { it.key.startsWith(name) }.forEach { entry ->
|
||||||
while(!currentName.isEmpty()) {
|
Platform.runLater {
|
||||||
//recursively update all upper level bindings
|
entry.value.invalidate()
|
||||||
bindings[currentName]?.invalidate()
|
|
||||||
currentName = currentName.cutLast()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// var currentName = name
|
||||||
|
// while (!currentName.isEmpty()) {
|
||||||
|
// //recursively update all upper level bindings
|
||||||
|
// bindings[currentName]?.invalidate()
|
||||||
|
// currentName = currentName.cutLast()
|
||||||
|
// }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun get(key: Name): ObjectBinding<MetaItem<*>?> {
|
operator fun get(key: Name): ObjectBinding<MetaItem<*>?> {
|
||||||
@ -46,9 +52,9 @@ fun ObjectBinding<MetaItem<*>?>.long() = objectBinding { it.long }
|
|||||||
fun ObjectBinding<MetaItem<*>?>.node() = objectBinding { it.node }
|
fun ObjectBinding<MetaItem<*>?>.node() = objectBinding { it.node }
|
||||||
|
|
||||||
fun ObjectBinding<MetaItem<*>?>.string(default: String) = stringBinding { it.string ?: default }
|
fun ObjectBinding<MetaItem<*>?>.string(default: String) = stringBinding { it.string ?: default }
|
||||||
fun ObjectBinding<MetaItem<*>?>.double(default: Double) = objectBinding { it.double ?: default }
|
fun ObjectBinding<MetaItem<*>?>.double(default: Double) = doubleBinding { it.double ?: default }
|
||||||
fun ObjectBinding<MetaItem<*>?>.float(default: Float) = objectBinding { it.float ?: default }
|
fun ObjectBinding<MetaItem<*>?>.float(default: Float) = floatBinding { it.float ?: default }
|
||||||
fun ObjectBinding<MetaItem<*>?>.int(default: Int) = objectBinding { it.int ?: default }
|
fun ObjectBinding<MetaItem<*>?>.int(default: Int) = integerBinding { it.int ?: default }
|
||||||
fun ObjectBinding<MetaItem<*>?>.long(default: Long) = objectBinding { it.long ?:default }
|
fun ObjectBinding<MetaItem<*>?>.long(default: Long) = longBinding { it.long ?: default }
|
||||||
|
|
||||||
fun <T> ObjectBinding<MetaItem<*>?>.transform(transform: (MetaItem<*>) -> T) = objectBinding { it?.let(transform) }
|
fun <T> ObjectBinding<MetaItem<*>?>.transform(transform: (MetaItem<*>) -> T) = objectBinding { it?.let(transform) }
|
||||||
|
@ -28,10 +28,9 @@ rootProject.name = "dataforge-vis"
|
|||||||
include(
|
include(
|
||||||
":dataforge-vis-common",
|
":dataforge-vis-common",
|
||||||
":wrappers",
|
":wrappers",
|
||||||
":dataforge-vis-fx",
|
|
||||||
":dataforge-vis-spatial",
|
":dataforge-vis-spatial",
|
||||||
":dataforge-vis-spatial-gdml",
|
":dataforge-vis-spatial-gdml",
|
||||||
":spatial-js-demo"
|
":spatial-demo"
|
||||||
)
|
)
|
||||||
|
|
||||||
//if(file("../dataforge-core").exists()) {
|
//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
|
package hep.dataforge.vis.spatial.demo
|
||||||
|
|
||||||
import hep.dataforge.context.AbstractPlugin
|
import hep.dataforge.context.Global
|
||||||
import hep.dataforge.context.Context
|
|
||||||
import hep.dataforge.context.PluginFactory
|
|
||||||
import hep.dataforge.context.PluginTag
|
|
||||||
import hep.dataforge.meta.Meta
|
import hep.dataforge.meta.Meta
|
||||||
import hep.dataforge.meta.buildMeta
|
|
||||||
import hep.dataforge.meta.get
|
import hep.dataforge.meta.get
|
||||||
import hep.dataforge.meta.string
|
import hep.dataforge.meta.string
|
||||||
import hep.dataforge.names.Name
|
import hep.dataforge.names.Name
|
||||||
import hep.dataforge.names.toName
|
|
||||||
import hep.dataforge.output.OutputManager
|
import hep.dataforge.output.OutputManager
|
||||||
import hep.dataforge.output.Renderer
|
import hep.dataforge.output.Renderer
|
||||||
import hep.dataforge.vis.common.VisualObject
|
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.ThreeCanvas
|
||||||
import hep.dataforge.vis.spatial.three.ThreePlugin
|
import hep.dataforge.vis.spatial.three.ThreePlugin
|
||||||
import hep.dataforge.vis.spatial.three.output
|
import hep.dataforge.vis.spatial.three.output
|
||||||
@ -25,31 +18,25 @@ import kotlinx.html.hr
|
|||||||
import kotlinx.html.id
|
import kotlinx.html.id
|
||||||
import kotlinx.html.js.div
|
import kotlinx.html.js.div
|
||||||
import kotlinx.html.span
|
import kotlinx.html.span
|
||||||
|
import org.w3c.dom.Element
|
||||||
import kotlin.browser.document
|
import kotlin.browser.document
|
||||||
import kotlin.dom.clear
|
import kotlin.dom.clear
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
class ThreeDemoGrid(meta: Meta) : AbstractPlugin(meta), OutputManager {
|
class ThreeDemoGrid(element: Element, meta: Meta = Meta.empty) : OutputManager {
|
||||||
override val tag: PluginTag get() = Companion.tag
|
|
||||||
|
|
||||||
private val gridRoot = document.create.div("row")
|
private val gridRoot = document.create.div("row")
|
||||||
private val outputs: MutableMap<Name, ThreeCanvas> = HashMap()
|
private val outputs: MutableMap<Name, ThreeCanvas> = HashMap()
|
||||||
|
|
||||||
init {
|
private val three = Global.plugins.fetch(ThreePlugin)
|
||||||
require(ThreePlugin)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun attach(context: Context) {
|
init {
|
||||||
super.attach(context)
|
|
||||||
val elementId = meta["elementID"].string ?: "canvas"
|
|
||||||
val element = document.getElementById(elementId) ?: error("Element with id $elementId not found on page")
|
|
||||||
element.clear()
|
element.clear()
|
||||||
element.append(gridRoot)
|
element.append(gridRoot)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
override fun <T : Any> get(type: KClass<out T>, name: Name, stage: Name, meta: Meta): Renderer<T> {
|
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) {
|
return outputs.getOrPut(name) {
|
||||||
if (type != VisualObject::class) error("Supports only DisplayObject")
|
if (type != VisualObject::class) error("Supports only DisplayObject")
|
||||||
@ -63,7 +50,7 @@ class ThreeDemoGrid(meta: Meta) : AbstractPlugin(meta), OutputManager {
|
|||||||
gridRoot.append {
|
gridRoot.append {
|
||||||
span("border") {
|
span("border") {
|
||||||
div("col-6") {
|
div("col-6") {
|
||||||
div { id = "output-$name" }.also{
|
div { id = "output-$name" }.also {
|
||||||
output.attach(it)
|
output.attach(it)
|
||||||
}
|
}
|
||||||
hr()
|
hr()
|
||||||
@ -75,20 +62,5 @@ class ThreeDemoGrid(meta: Meta) : AbstractPlugin(meta), OutputManager {
|
|||||||
output
|
output
|
||||||
} as Renderer<T>
|
} 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>()
|
||||||
|
}
|
@ -48,11 +48,3 @@ class FXDemoGrid : View(), OutputManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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