diff --git a/README.md b/README.md index 783d9995..83021789 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +[![JetBrains Research](https://jb.gg/badges/research.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub) + # DataForge plugins for visualisation ## Common visualisation objects diff --git a/build.gradle.kts b/build.gradle.kts index 61a73e71..f396caad 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,8 +1,10 @@ +import scientifik.useSerialization + val dataforgeVersion by extra("0.1.5-dev-4") plugins { val kotlinVersion = "1.3.61" - val toolsVersion = "0.2.7" + val toolsVersion = "0.3.1" kotlin("jvm") version kotlinVersion apply false id("kotlin-dce-js") version kotlinVersion apply false @@ -27,6 +29,10 @@ allprojects { version = "0.1.0-dev" } +subprojects{ + useSerialization("0.13.0") +} + val githubProject by extra("dataforge-vis") val bintrayRepo by extra("dataforge") diff --git a/dataforge-vis-common/build.gradle.kts b/dataforge-vis-common/build.gradle.kts index e6fb3815..767364de 100644 --- a/dataforge-vis-common/build.gradle.kts +++ b/dataforge-vis-common/build.gradle.kts @@ -5,10 +5,6 @@ plugins { id("org.openjfx.javafxplugin") } -scientifik{ - withSerialization() -} - val dataforgeVersion: String by rootProject.extra //val kvisionVersion: String by rootProject.extra("2.0.0-M1") diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/AbstractVisualGroup.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/AbstractVisualGroup.kt index 921617c8..21edc5e8 100644 --- a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/AbstractVisualGroup.kt +++ b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/AbstractVisualGroup.kt @@ -1,8 +1,10 @@ package hep.dataforge.vis.common -import hep.dataforge.meta.Meta import hep.dataforge.meta.MetaItem -import hep.dataforge.names.* +import hep.dataforge.names.Name +import hep.dataforge.names.NameToken +import hep.dataforge.names.asName +import hep.dataforge.names.isEmpty import kotlinx.serialization.Transient @@ -16,46 +18,7 @@ abstract class AbstractVisualGroup : AbstractVisualObject(), MutableVisualGroup /** * A map of top level named children */ - abstract override val children: Map //get() = _children - - /** - * Styles, defined in this group. A style could be defined but not applied - * TODO replace by custom object with get/set functionality - */ - protected abstract val styleSheet: MutableMap - - override fun getStyle(name: Name): Meta? = styleSheet[name] - - override fun addStyle(name: Name, meta: Meta, apply: Boolean) { - fun VisualObject.applyStyle(name: Name, meta: Meta) { - if (styles.contains(name)) { - //full update - //TODO do a fine grained update - if (this is AbstractVisualObject) { - styleChanged() - } else { - propertyChanged(EmptyName) - } - } - if (this is VisualGroup) { - this.children.forEach { (_, child) -> - child.applyStyle(name, meta) - } - } - } - styleSheet[name] = meta - if (apply) { - applyStyle(name, meta) - } - } - - -// init { -// //Do after deserialization -// children.values.forEach { -// it.parent = this -// } -// } + abstract override val children: Map override fun propertyChanged(name: Name, before: MetaItem<*>?, after: MetaItem<*>?) { super.propertyChanged(name, before, after) diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/AbstractVisualObject.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/AbstractVisualObject.kt index e0808e75..b59f2564 100644 --- a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/AbstractVisualObject.kt +++ b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/AbstractVisualObject.kt @@ -1,9 +1,8 @@ package hep.dataforge.vis.common import hep.dataforge.meta.* -import hep.dataforge.names.EmptyName import hep.dataforge.names.Name -import hep.dataforge.names.toName +import hep.dataforge.names.asName import hep.dataforge.vis.common.VisualObject.Companion.STYLE_KEY import kotlinx.serialization.Transient @@ -17,15 +16,25 @@ abstract class AbstractVisualObject : VisualObject { @Transient override var parent: VisualObject? = null - abstract override var properties: Config? + protected abstract var properties: Config? - override var styles: List - get() = properties?.get(STYLE_KEY).stringList.map(String::toName) + override var styles: List + get() = properties?.get(STYLE_KEY).stringList set(value) { - setProperty(STYLE_KEY, value.map { it.toString() }) - styleChanged() + //val allStyles = (field + value).distinct() + setProperty(STYLE_KEY, value) + updateStyles(value) } + protected fun updateStyles(names: List) { + names.mapNotNull { findStyle(it) }.asSequence() + .flatMap { it.items.asSequence() } + .distinctBy { it.key } + .forEach { + propertyChanged(it.key.asName(), null, it.value) + } + } + /** * The config is initialized and assigned on-demand. * To avoid unnecessary allocations, one should access [properties] via [getProperty] instead. @@ -67,14 +76,6 @@ abstract class AbstractVisualObject : VisualObject { } - /** - * Helper to reset style cache - */ - protected fun styleChanged() { - styleCache = null - propertyChanged(EmptyName) - } - override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? { return if (inherit) { properties?.get(name) ?: mergedStyles[name] ?: parent?.getProperty(name, inherit) @@ -84,10 +85,10 @@ abstract class AbstractVisualObject : VisualObject { } } -fun VisualObject.findStyle(styleName: Name): Meta? { - if (this is VisualGroup) { - val style = getStyle(styleName) - if (style != null) return style - } - return parent?.findStyle(styleName) -} \ No newline at end of file +//fun VisualObject.findStyle(styleName: Name): Meta? { +// if (this is VisualGroup) { +// val style = resolveStyle(styleName) +// if (style != null) return style +// } +// return parent?.findStyle(styleName) +//} \ No newline at end of file diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/Colors.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/Colors.kt index 780a6874..7577fa33 100644 --- a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/Colors.kt +++ b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/Colors.kt @@ -1,5 +1,10 @@ package hep.dataforge.vis.common +import hep.dataforge.meta.* +import hep.dataforge.values.ValueType +import hep.dataforge.values.int +import kotlin.math.max + /** * Taken from https://github.com/markaren/three.kt/blob/master/threejs-wrapper/src/main/kotlin/info/laht/threekt/math/ColorConstants.kt */ @@ -175,9 +180,38 @@ object Colors { const val yellow = 0xFFFF00 const val yellowgreen = 0x9ACD32 + const val RED_KEY = "red" + const val GREEN_KEY = "green" + const val BLUE_KEY = "blue" + + fun fromMeta(item: MetaItem<*>): String { + return when (item) { + is MetaItem.NodeItem<*> -> { + val node = item.node + rgbToString( + node[RED_KEY].number?.toByte()?.toUByte() ?: 0u, + node[GREEN_KEY].number?.toByte()?.toUByte() ?: 0u, + node[BLUE_KEY].number?.toByte()?.toUByte() ?: 0u + ) + } + is MetaItem.ValueItem -> { + if (item.value.type == ValueType.NUMBER) { + rgbToString(item.value.int) + } else { + item.value.string + } + } + } + } + + fun rgbToString(rgb: Int): String { + val string = rgb.toString(16).padStart(6, '0') + return "#" + string.substring(max(0, string.length - 6)) + } + fun rgbToString(red: UByte, green: UByte, blue: UByte): String { - fun colorToString(color: UByte): String{ - return color.toString(16).padStart(2,'0') + fun colorToString(color: UByte): String { + return color.toString(16).padStart(2, '0') } return buildString { append("#") @@ -186,4 +220,10 @@ object Colors { append(colorToString(blue)) } } + + fun rgbToMeta(r: UByte, g: UByte, b: UByte): Meta = buildMeta { + RED_KEY put r.toInt() + GREEN_KEY put g.toInt() + BLUE_KEY put b.toInt() + } } \ No newline at end of file diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/StyleSheet.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/StyleSheet.kt new file mode 100644 index 00000000..d6803562 --- /dev/null +++ b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/StyleSheet.kt @@ -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() + + val items: Map 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 = ((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) } + } +} \ No newline at end of file diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/MutableVisualGroup.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualGroup.kt similarity index 69% rename from dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/MutableVisualGroup.kt rename to dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualGroup.kt index 1c99e6f6..5baa6262 100644 --- a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/MutableVisualGroup.kt +++ b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualGroup.kt @@ -1,6 +1,8 @@ package hep.dataforge.vis.common -import hep.dataforge.meta.Meta +import hep.dataforge.meta.MetaBuilder +import hep.dataforge.meta.buildMeta +import hep.dataforge.meta.seal import hep.dataforge.names.* import hep.dataforge.provider.Provider @@ -12,6 +14,8 @@ interface VisualGroup : Provider, Iterable, VisualObject { override val defaultTarget: String get() = VisualObject.TYPE + val styleSheet: StyleSheet + override fun provideTop(target: String): Map = when (target) { VisualObject.TYPE -> children.flatMap { (key, value) -> @@ -22,7 +26,7 @@ interface VisualGroup : Provider, Iterable, VisualObject { } res.entries }.associate { it.toPair() } - //TODO add styles + STYLE_TARGET -> styleSheet.items.mapKeys { it.key.toName() } else -> emptyMap() } @@ -32,17 +36,6 @@ interface VisualGroup : Provider, Iterable, VisualObject { */ override fun iterator(): Iterator = children.values.iterator() - /** - * Resolve style by its name - * TODO change to Config? - */ - fun getStyle(name: Name): Meta? - - /** - * Add or replace style with given name - */ - fun addStyle(name: Name, meta: Meta, apply: Boolean = true) - operator fun get(name: Name): VisualObject? { return when { name.isEmpty() -> this @@ -50,8 +43,30 @@ interface VisualGroup : Provider, Iterable, VisualObject { else -> (children[name.first()!!] as? VisualGroup)?.get(name.cutFirst()) } } + + /** + * A fix for serialization bug that writes all proper parents inside the tree after deserialization + */ + fun attachChildren(){ + styleSheet.owner = this + this.children.values.forEach { + it.parent = this + (it as? VisualGroup)?.attachChildren() + } + } + + companion object { + const val STYLE_TARGET = "style" + } } +fun VisualGroup.updateStyle(key: String, builder: MetaBuilder.() -> Unit) { + val newStyle = styleSheet[key]?.let { buildMeta(it, builder) } ?: buildMeta(builder) + styleSheet[key] = newStyle.seal() +} + +data class StyleRef(val group: VisualGroup, val styleName: Name) + val VisualGroup.isEmpty: Boolean get() = this.children.isEmpty() interface MutableVisualGroup : VisualGroup { diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualObject.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualObject.kt index 23d46683..008ecd9d 100644 --- a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualObject.kt +++ b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualObject.kt @@ -1,9 +1,6 @@ package hep.dataforge.vis.common -import hep.dataforge.meta.Config -import hep.dataforge.meta.Configurable -import hep.dataforge.meta.Laminate -import hep.dataforge.meta.MetaItem +import hep.dataforge.meta.* import hep.dataforge.names.Name import hep.dataforge.names.asName import hep.dataforge.names.toName @@ -26,11 +23,6 @@ interface VisualObject : Configurable { @Transient var parent: VisualObject? - /** - * Direct properties access - */ - val properties: Config? - /** * Set property for this object */ @@ -42,9 +34,11 @@ interface VisualObject : Configurable { fun getProperty(name: Name, inherit: Boolean = true): MetaItem<*>? /** - * Manually trigger property changed event. If [name] is empty, notify that the whole object is changed + * Trigger property invalidation event. If [name] is empty, notify that the whole object is changed */ - fun propertyChanged(name: Name, before: MetaItem<*>? = null, after: MetaItem<*>? = null): Unit + fun propertyChanged(name: Name, before: MetaItem<*>?, after: MetaItem<*>?): Unit + + fun propertyInvalidated(name: Name) = propertyChanged(name, null, null) /** * Add listener triggering on property change @@ -57,11 +51,9 @@ interface VisualObject : Configurable { fun removeChangeListener(owner: Any?) /** - * List of names of styles applied to this object. Order matters. + * List of names of styles applied to this object. Order matters. Not inherited */ - var styles: List - - fun findAllStyles(): Laminate = Laminate(styles.distinct().mapNotNull(::findStyle)) + var styles: List companion object { const val TYPE = "visual" @@ -69,12 +61,42 @@ interface VisualObject : Configurable { //const val META_KEY = "@meta" //const val TAGS_KEY = "@tags" + + } } fun VisualObject.getProperty(key: String, inherit: Boolean = true): MetaItem<*>? = getProperty(key.toName(), inherit) fun VisualObject.setProperty(key: String, value: Any?) = setProperty(key.toName(), value) -fun VisualObject.applyStyle(name: String) { - styles = styles + name.toName() -} \ No newline at end of file +/** + * 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)) + diff --git a/dataforge-vis-spatial-gdml/build.gradle.kts b/dataforge-vis-spatial-gdml/build.gradle.kts index 31c0f223..2d8a4200 100644 --- a/dataforge-vis-spatial-gdml/build.gradle.kts +++ b/dataforge-vis-spatial-gdml/build.gradle.kts @@ -1,32 +1,20 @@ -import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpack - plugins { id("scientifik.mpp") } -scientifik{ - withSerialization() -} - kotlin { sourceSets { val commonMain by getting { dependencies { api(project(":dataforge-vis-spatial")) - api("scientifik:gdml:0.1.3") - } - } - val jsMain by getting { - dependencies { - api(project(":dataforge-vis-spatial")) - //api("kotlin.js.externals:kotlin-js-jquery:3.2.0-0") + api("scientifik:gdml:0.1.4") } } } } -tasks{ - val jsBrowserWebpack by getting(KotlinWebpack::class) { - sourceMaps = false - } -} \ No newline at end of file +//tasks{ +// val jsBrowserWebpack by getting(KotlinWebpack::class) { +// sourceMaps = false +// } +//} \ No newline at end of file diff --git a/dataforge-vis-spatial-gdml/src/commonMain/kotlin/hep/dataforge/vis/spatial/gdml/GDMLTransformer.kt b/dataforge-vis-spatial-gdml/src/commonMain/kotlin/hep/dataforge/vis/spatial/gdml/GDMLTransformer.kt index 698c5ada..3b529780 100644 --- a/dataforge-vis-spatial-gdml/src/commonMain/kotlin/hep/dataforge/vis/spatial/gdml/GDMLTransformer.kt +++ b/dataforge-vis-spatial-gdml/src/commonMain/kotlin/hep/dataforge/vis/spatial/gdml/GDMLTransformer.kt @@ -6,8 +6,8 @@ import hep.dataforge.meta.buildMeta import hep.dataforge.names.Name import hep.dataforge.names.toName import hep.dataforge.vis.common.VisualObject -import hep.dataforge.vis.common.applyStyle -import hep.dataforge.vis.spatial.Material3D.Companion.COLOR_KEY +import hep.dataforge.vis.common.useStyle +import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_COLOR_KEY import hep.dataforge.vis.spatial.RotationOrder import hep.dataforge.vis.spatial.VisualGroup3D import hep.dataforge.vis.spatial.VisualObject3D @@ -43,7 +43,7 @@ class GDMLTransformer(val root: GDML) { styleCache.getOrPut(name.toName()){ buildMeta(builder) } - applyStyle(name) + useStyle(name) } internal fun configureSolid(obj: VisualObject3D, parent: GDMLVolume, solid: GDMLSolid) { @@ -52,7 +52,7 @@ class GDMLTransformer(val root: GDML) { val styleName = "material[${material.name}]" obj.useStyle(styleName){ - COLOR_KEY to random.nextInt(0, Int.MAX_VALUE) + MATERIAL_COLOR_KEY put random.nextInt(16777216) "gdml.material" put material.name } @@ -68,7 +68,7 @@ class GDMLTransformer(val root: GDML) { internal fun finalize(final: VisualGroup3D): VisualGroup3D { final.prototypes = proto styleCache.forEach { - final.addStyle(it.key, it.value, false) + final.styleSheet[it.key.toString()] = it.value } final.rotationOrder = RotationOrder.ZXY onFinish(this@GDMLTransformer) diff --git a/dataforge-vis-spatial-gdml/src/commonMain/kotlin/hep/dataforge/vis/spatial/gdml/visualGDML.kt b/dataforge-vis-spatial-gdml/src/commonMain/kotlin/hep/dataforge/vis/spatial/gdml/visualGDML.kt index 97b6b474..42c3d1c6 100644 --- a/dataforge-vis-spatial-gdml/src/commonMain/kotlin/hep/dataforge/vis/spatial/gdml/visualGDML.kt +++ b/dataforge-vis-spatial-gdml/src/commonMain/kotlin/hep/dataforge/vis/spatial/gdml/visualGDML.kt @@ -244,4 +244,11 @@ fun GDML.toVisual(block: GDMLTransformer.() -> Unit = {}): VisualGroup3D { val context = GDMLTransformer(this).apply(block) return context.finalize(volume(context, world)) +} + +/** + * Append gdml node to the group + */ +fun VisualGroup3D.gdml(gdml: GDML, key: String = "", transformer: GDMLTransformer.() -> Unit = {}) { + set(key, gdml.toVisual(transformer)) } \ No newline at end of file diff --git a/dataforge-vis-spatial-gdml/src/jsMain/kotlin/hep/dataforge/vis/spatial/gdml/demo/GDMLDemoApp.kt b/dataforge-vis-spatial-gdml/src/jsMain/kotlin/hep/dataforge/vis/spatial/gdml/demo/GDMLDemoApp.kt index 67d4d184..1c270083 100644 --- a/dataforge-vis-spatial-gdml/src/jsMain/kotlin/hep/dataforge/vis/spatial/gdml/demo/GDMLDemoApp.kt +++ b/dataforge-vis-spatial-gdml/src/jsMain/kotlin/hep/dataforge/vis/spatial/gdml/demo/GDMLDemoApp.kt @@ -5,11 +5,10 @@ import hep.dataforge.js.Application import hep.dataforge.js.objectTree import hep.dataforge.js.startApplication import hep.dataforge.names.NameToken -import hep.dataforge.vis.spatial.Material3D.Companion.OPACITY_KEY +import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_OPACITY_KEY import hep.dataforge.vis.spatial.Visual3DPlugin import hep.dataforge.vis.spatial.VisualGroup3D import hep.dataforge.vis.spatial.VisualObject3D -import hep.dataforge.vis.spatial.attachChildren import hep.dataforge.vis.spatial.editor.propertyEditor import hep.dataforge.vis.spatial.editor.threeOutputConfig import hep.dataforge.vis.spatial.gdml.GDMLTransformer @@ -106,7 +105,7 @@ private class GDMLDemoApp : Application { || parent.physVolumes.isNotEmpty() ) { useStyle("opaque") { - OPACITY_KEY to 0.3 + MATERIAL_OPACITY_KEY put 0.3 } } } @@ -154,7 +153,7 @@ private class GDMLDemoApp : Application { output.camera.layers.set(0) configElement.threeOutputConfig(output) //tree.visualObjectTree(visual, editor::propertyEditor) - treeElement.objectTree(NameToken("World"),visual, editorElement::propertyEditor) + treeElement.objectTree(NameToken("World"), visual, editorElement::propertyEditor) output.render(visual) diff --git a/dataforge-vis-spatial-gdml/src/jvmMain/kotlin/hep/dataforge/vis/spatial/gdml/testCoil.kt b/dataforge-vis-spatial-gdml/src/jvmMain/kotlin/hep/dataforge/vis/spatial/gdml/testCoil.kt deleted file mode 100644 index 567f0e7c..00000000 --- a/dataforge-vis-spatial-gdml/src/jvmMain/kotlin/hep/dataforge/vis/spatial/gdml/testCoil.kt +++ /dev/null @@ -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())) -} \ No newline at end of file diff --git a/dataforge-vis-spatial-gdml/src/jvmMain/kotlin/hep/dataforge/vis/spatial/gdml/testCubes.kt b/dataforge-vis-spatial-gdml/src/jvmMain/kotlin/hep/dataforge/vis/spatial/gdml/testCubes.kt deleted file mode 100644 index b0ff5b38..00000000 --- a/dataforge-vis-spatial-gdml/src/jvmMain/kotlin/hep/dataforge/vis/spatial/gdml/testCubes.kt +++ /dev/null @@ -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) -} \ No newline at end of file diff --git a/dataforge-vis-spatial-gdml/src/jvmMain/kotlin/hep/dataforge/vis/spatial/gdml/testJson.kt b/dataforge-vis-spatial-gdml/src/jvmMain/kotlin/hep/dataforge/vis/spatial/gdml/testJson.kt deleted file mode 100644 index ad722578..00000000 --- a/dataforge-vis-spatial-gdml/src/jvmMain/kotlin/hep/dataforge/vis/spatial/gdml/testJson.kt +++ /dev/null @@ -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) -} \ No newline at end of file diff --git a/dataforge-vis-spatial-gdml/src/jvmMain/kotlin/hep/dataforge/vis/spatial/gdml/testMain.kt b/dataforge-vis-spatial-gdml/src/jvmMain/kotlin/hep/dataforge/vis/spatial/gdml/testMain.kt deleted file mode 100644 index 7ef8a918..00000000 --- a/dataforge-vis-spatial-gdml/src/jvmMain/kotlin/hep/dataforge/vis/spatial/gdml/testMain.kt +++ /dev/null @@ -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) -} \ No newline at end of file diff --git a/dataforge-vis-spatial-gdml/src/jvmMain/kotlin/hep/dataforge/vis/spatial/gdml/visualGDMLJvm.kt b/dataforge-vis-spatial-gdml/src/jvmMain/kotlin/hep/dataforge/vis/spatial/gdml/visualGDMLJvm.kt new file mode 100644 index 00000000..e6095e47 --- /dev/null +++ b/dataforge-vis-spatial-gdml/src/jvmMain/kotlin/hep/dataforge/vis/spatial/gdml/visualGDMLJvm.kt @@ -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) +} \ No newline at end of file diff --git a/dataforge-vis-spatial/build.gradle.kts b/dataforge-vis-spatial/build.gradle.kts index 203cd2b7..92033405 100644 --- a/dataforge-vis-spatial/build.gradle.kts +++ b/dataforge-vis-spatial/build.gradle.kts @@ -5,10 +5,6 @@ plugins { id("org.openjfx.javafxplugin") } -scientifik { - withSerialization() -} - kotlin { jvm { withJava() @@ -21,9 +17,13 @@ kotlin { } jvmMain { dependencies { - api("org.fxyz3d:fxyz3d:0.5.2") + implementation("org.fxyz3d:fxyz3d:0.5.2") { + exclude(module = "slf4j-simple") + } api("org.jetbrains.kotlinx:kotlinx-coroutines-javafx:${Scientifik.coroutinesVersion}") - implementation("eu.mihosoft.vrl.jcsg:jcsg:0.5.7") + implementation("eu.mihosoft.vrl.jcsg:jcsg:0.5.7") { + exclude(module = "slf4j-simple") + } } } jsMain { diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Composite.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Composite.kt index e0ac521b..2ea3fc31 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Composite.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Composite.kt @@ -1,4 +1,5 @@ @file:UseSerializers(Point3DSerializer::class) + package hep.dataforge.vis.spatial import hep.dataforge.io.serialization.ConfigSerializer @@ -43,10 +44,9 @@ inline fun VisualGroup3D.composite( val children = group.filterIsInstance() if (children.size != 2) error("Composite requires exactly two children") return Composite(type, children[0], children[1]).also { - if (group.properties != null) { - it.config.update(group.config) - it.material = group.material - } + it.config.update(group.config) + //it.material = group.material + it.position = group.position it.rotation = group.rotation it.scale = group.scale diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Text3D.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Label3D.kt similarity index 58% rename from dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Text3D.kt rename to dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Label3D.kt index 2fbecf1d..01ff005e 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Text3D.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Label3D.kt @@ -9,7 +9,8 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.UseSerializers @Serializable -class Text3D(var text: String, var fontSize: Int) : AbstractVisualObject(), VisualObject3D { +class Label3D(var text: String, var fontSize: Double, var fontFamily: String) : AbstractVisualObject(), + VisualObject3D { @Serializable(ConfigSerializer::class) override var properties: Config? = null @@ -19,5 +20,11 @@ class Text3D(var text: String, var fontSize: Int) : AbstractVisualObject(), Visu } -fun VisualGroup3D.text(text: String, fontSize: Int, name: String = "", action: Text3D.() -> Unit = {}) = - Text3D(text, fontSize).apply(action).also { set(name, it) } \ No newline at end of file +fun VisualGroup3D.label( + text: String, + fontSize: Number = 20, + fontFamily: String = "Arial", + name: String = "", + action: Label3D.() -> Unit = {} +) = + Label3D(text, fontSize.toDouble(), fontFamily).apply(action).also { set(name, it) } \ No newline at end of file diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Material3D.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Material3D.kt index 68913c55..40520f0f 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Material3D.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Material3D.kt @@ -3,63 +3,72 @@ package hep.dataforge.vis.spatial import hep.dataforge.meta.* import hep.dataforge.names.asName import hep.dataforge.names.plus -import hep.dataforge.vis.common.VisualObject -import hep.dataforge.vis.spatial.Material3D.Companion.COLOR_KEY -import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_KEY -import hep.dataforge.vis.spatial.Material3D.Companion.OPACITY_KEY +import hep.dataforge.vis.common.Colors +import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_COLOR_KEY +import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_OPACITY_KEY class Material3D(override val config: Config) : Specific { - var color by string() + var color by string(key = COLOR_KEY) - var opacity by float(1f) + var specularColor by string() + + var opacity by float(1f, key = OPACITY_KEY) + + var wireframe by boolean(false, WIREFRAME_KEY) companion object : Specification { override fun wrap(config: Config): Material3D = Material3D(config) val MATERIAL_KEY = "material".asName() - val COLOR_KEY = MATERIAL_KEY + "color" - val SPECULAR_COLOR = MATERIAL_KEY + "specularColor" - val OPACITY_KEY = MATERIAL_KEY + "opacity" + internal val COLOR_KEY = "color".asName() + val MATERIAL_COLOR_KEY = MATERIAL_KEY + COLOR_KEY + val SPECULAR_COLOR ="specularColor".asName() + internal val OPACITY_KEY = "opacity".asName() + val MATERIAL_OPACITY_KEY = MATERIAL_KEY + OPACITY_KEY + internal val WIREFRAME_KEY = "wireframe".asName() + val MATERIAL_WIREFRAME_KEY = MATERIAL_KEY + WIREFRAME_KEY } } -fun VisualObject.color(rgb: String) { - setProperty(COLOR_KEY, rgb) +fun VisualObject3D.color(rgb: String) { + setProperty(MATERIAL_COLOR_KEY, rgb) } -fun VisualObject.color(rgb: Int) { - setProperty(COLOR_KEY, rgb) +fun VisualObject3D.color(rgb: Int) { + setProperty(MATERIAL_COLOR_KEY, rgb) } -fun VisualObject.color(r: UByte, g: UByte, b: UByte) = setProperty( - COLOR_KEY, - buildMeta { - "red" put r.toInt() - "green" put g.toInt() - "blue" put b.toInt() - } +fun VisualObject3D.color(r: UByte, g: UByte, b: UByte) = setProperty( + MATERIAL_COLOR_KEY, + Colors.rgbToMeta(r, g, b) ) -var VisualObject.color: String? - get() = getProperty(COLOR_KEY).string +/** + * Web colors representation of the color in `#rrggbb` format or HTML name + */ +var VisualObject3D.color: String? + get() = getProperty(MATERIAL_COLOR_KEY)?.let { Colors.fromMeta(it) } set(value) { - if (value != null) { - color(value) - } + setProperty(MATERIAL_COLOR_KEY, value) } -var VisualObject.material: Material3D? - get() = getProperty(MATERIAL_KEY).node?.let { Material3D.wrap(it) } - set(value) = setProperty(MATERIAL_KEY, value?.config) +//var VisualObject3D.material: Material3D? +// get() = getProperty(MATERIAL_KEY).node?.let { Material3D.wrap(it) } +// set(value) = setProperty(MATERIAL_KEY, value?.config) -fun VisualObject.material(builder: Material3D.() -> Unit) { - material = Material3D.build(builder) +fun VisualObject3D.material(builder: Material3D.() -> Unit) { + val node = config[Material3D.MATERIAL_KEY].node + if (node != null) { + Material3D.update(node, builder) + } else { + config[Material3D.MATERIAL_KEY] = Material3D.build(builder) + } } -var VisualObject.opacity: Double? - get() = getProperty(OPACITY_KEY).double +var VisualObject3D.opacity: Double? + get() = getProperty(MATERIAL_OPACITY_KEY).double set(value) { - setProperty(OPACITY_KEY, value) + setProperty(MATERIAL_OPACITY_KEY, value) } \ No newline at end of file diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Proxy.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Proxy.kt index e71186c7..b3b04cd0 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Proxy.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Proxy.kt @@ -5,17 +5,13 @@ package hep.dataforge.vis.spatial import hep.dataforge.io.serialization.ConfigSerializer import hep.dataforge.io.serialization.NameSerializer import hep.dataforge.meta.Config -import hep.dataforge.meta.Meta import hep.dataforge.meta.MetaItem import hep.dataforge.meta.get import hep.dataforge.names.Name import hep.dataforge.names.NameToken import hep.dataforge.names.asName import hep.dataforge.names.plus -import hep.dataforge.vis.common.AbstractVisualObject -import hep.dataforge.vis.common.MutableVisualGroup -import hep.dataforge.vis.common.VisualGroup -import hep.dataforge.vis.common.VisualObject +import hep.dataforge.vis.common.* import kotlinx.serialization.Serializable import kotlinx.serialization.Transient import kotlinx.serialization.UseSerializers @@ -43,12 +39,8 @@ class Proxy(val templateName: Name) : AbstractVisualObject(), VisualGroup, Visua get() = (parent as? VisualGroup3D)?.getPrototype(templateName) ?: error("Template with name $templateName not found in $parent") - override fun getStyle(name: Name): Meta? = (parent as VisualGroup?)?.getStyle(name) - - override fun addStyle(name: Name, meta: Meta, apply: Boolean) { - (parent as VisualGroup?)?.addStyle(name, meta, apply) - //do nothing - } + override val styleSheet: StyleSheet + get() = (parent as? VisualGroup)?.styleSheet ?: StyleSheet(this) override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? { return if (inherit) { @@ -77,16 +69,17 @@ class Proxy(val templateName: Name) : AbstractVisualObject(), VisualGroup, Visua return NameToken(PROXY_CHILD_PROPERTY_PREFIX, childName.toString()) + propertyName } - private fun prototypeFor(name: Name): VisualObject = - (prototype as? VisualGroup)?.get(name) - ?: error("Prototype with name $name not found in ${this@Proxy}") + private fun prototypeFor(name: Name): VisualObject { + return (prototype as? VisualGroup)?.get(name) + ?: error("Prototype with name $name not found in $this") + } - override var styles: List + override var styles: List get() = super.styles + prototype.styles set(value) { - setProperty(VisualObject.STYLE_KEY, value.map { it.toString() }) - styleChanged() + setProperty(VisualObject.STYLE_KEY, value) + updateStyles(value) } //override fun findAllStyles(): Laminate = Laminate((styles + prototype.styles).mapNotNull { findStyle(it) }) @@ -94,9 +87,9 @@ class Proxy(val templateName: Name) : AbstractVisualObject(), VisualGroup, Visua @Serializable inner class ProxyChild(val name: Name) : AbstractVisualObject(), VisualGroup { - val prototype: VisualObject by lazy { - prototypeFor(name) - } + val prototype: VisualObject get() = prototypeFor(name) + + override val styleSheet: StyleSheet get() = this@Proxy.styleSheet override val children: Map get() = (prototype as? VisualGroup)?.children?.mapValues { (key, _) -> @@ -105,12 +98,6 @@ class Proxy(val templateName: Name) : AbstractVisualObject(), VisualGroup, Visua ) } ?: emptyMap() - override fun getStyle(name: Name): Meta? = this@Proxy.getStyle(name) - - override fun addStyle(name: Name, meta: Meta, apply: Boolean) { - this@Proxy.addStyle(name, meta, apply) - } - override var properties: Config? get() = propertyCache[name] set(value) { @@ -176,8 +163,8 @@ fun VisualGroup3D.proxy( ): Proxy { val existing = getPrototype(templateName) if (existing == null) { - setPrototype(templateName,obj, attachToParent) - } else if(existing != obj) { + setPrototype(templateName, obj, attachToParent) + } else if (existing != obj) { error("Can't add different prototype on top of existing one") } return ref(templateName, name, block) diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/VisualGroup3D.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/VisualGroup3D.kt index 9c959e73..bb76d972 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/VisualGroup3D.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/VisualGroup3D.kt @@ -12,12 +12,12 @@ import hep.dataforge.io.serialization.ConfigSerializer import hep.dataforge.io.serialization.MetaSerializer import hep.dataforge.io.serialization.NameSerializer import hep.dataforge.meta.Config -import hep.dataforge.meta.Meta import hep.dataforge.names.Name import hep.dataforge.names.NameToken import hep.dataforge.names.asName import hep.dataforge.names.isEmpty import hep.dataforge.vis.common.AbstractVisualGroup +import hep.dataforge.vis.common.StyleSheet import hep.dataforge.vis.common.VisualGroup import hep.dataforge.vis.common.VisualObject import kotlinx.serialization.SerialName @@ -37,9 +37,10 @@ class VisualGroup3D : AbstractVisualGroup(), VisualObject3D { field = value } + override val styleSheet: StyleSheet = StyleSheet(this) + //FIXME to be lifted to AbstractVisualGroup after https://github.com/Kotlin/kotlinx.serialization/issues/378 is fixed override var properties: Config? = null - override val styleSheet = HashMap() override var position: Point3D? = null override var rotation: Point3D? = null @@ -49,6 +50,11 @@ class VisualGroup3D : AbstractVisualGroup(), VisualObject3D { private val _children = HashMap() override val children: Map get() = _children + init { + //Do after deserialization + attachChildren() + } + override fun removeChild(token: NameToken) { _children.remove(token) childrenChanged(token.asName(), null) @@ -96,26 +102,18 @@ class VisualGroup3D : AbstractVisualGroup(), VisualObject3D { } } + override fun attachChildren() { + super.attachChildren() + prototypes?.run { + parent = this + attachChildren() + } + } + companion object { const val PROTOTYPES_KEY = "templates" } } -/** - * A fix for serialization bug that writes all proper parents inside the tree after deserialization - */ -fun VisualGroup.attachChildren() { - this.children.values.forEach { - it.parent = this - (it as? VisualGroup)?.attachChildren() - } - if (this is VisualGroup3D) { - prototypes?.also { - it.parent = this - it.attachChildren() - } - } -} - fun VisualGroup3D.group(key: String = "", action: VisualGroup3D.() -> Unit = {}): VisualGroup3D = VisualGroup3D().apply(action).also { set(key, it) } \ No newline at end of file diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/VisualObject3D.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/VisualObject3D.kt index 7d2a2cac..4a07c260 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/VisualObject3D.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/VisualObject3D.kt @@ -129,21 +129,21 @@ var VisualObject3D.x: Number get() = position?.x ?: 0f set(value) { position().x = value.toDouble() - propertyChanged(VisualObject3D.xPos) + propertyInvalidated(VisualObject3D.xPos) } var VisualObject3D.y: Number get() = position?.y ?: 0f set(value) { position().y = value.toDouble() - propertyChanged(VisualObject3D.yPos) + propertyInvalidated(VisualObject3D.yPos) } var VisualObject3D.z: Number get() = position?.z ?: 0f set(value) { position().z = value.toDouble() - propertyChanged(VisualObject3D.zPos) + propertyInvalidated(VisualObject3D.zPos) } private fun VisualObject3D.rotation(): Point3D = @@ -153,21 +153,21 @@ var VisualObject3D.rotationX: Number get() = rotation?.x ?: 0f set(value) { rotation().x = value.toDouble() - propertyChanged(VisualObject3D.xRotation) + propertyInvalidated(VisualObject3D.xRotation) } var VisualObject3D.rotationY: Number get() = rotation?.y ?: 0f set(value) { rotation().y = value.toDouble() - propertyChanged(VisualObject3D.yRotation) + propertyInvalidated(VisualObject3D.yRotation) } var VisualObject3D.rotationZ: Number get() = rotation?.z ?: 0f set(value) { rotation().z = value.toDouble() - propertyChanged(VisualObject3D.zRotation) + propertyInvalidated(VisualObject3D.zRotation) } private fun VisualObject3D.scale(): Point3D = @@ -177,19 +177,19 @@ var VisualObject3D.scaleX: Number get() = scale?.x ?: 1f set(value) { scale().x = value.toDouble() - propertyChanged(VisualObject3D.xScale) + propertyInvalidated(VisualObject3D.xScale) } var VisualObject3D.scaleY: Number get() = scale?.y ?: 1f set(value) { scale().y = value.toDouble() - propertyChanged(VisualObject3D.yScale) + propertyInvalidated(VisualObject3D.yScale) } var VisualObject3D.scaleZ: Number get() = scale?.z ?: 1f set(value) { scale().z = value.toDouble() - propertyChanged(VisualObject3D.zScale) + propertyInvalidated(VisualObject3D.zScale) } \ No newline at end of file diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/geometry.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/geometry.kt index 4ef1510c..e9fab6ab 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/geometry.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/geometry.kt @@ -14,8 +14,8 @@ operator fun Point2D.component1() = x operator fun Point2D.component2() = y fun Point2D.toMeta() = buildMeta { - VisualObject3D.x to x - VisualObject3D.y to y + VisualObject3D.x put x + VisualObject3D.y put y } fun Meta.point2D() = Point2D(this["x"].number ?: 0, this["y"].number ?: 0) @@ -53,7 +53,7 @@ fun Meta.point3D() = Point3D(this["x"].number ?: 0, this["y"].number ?: 0, this[ val zero = Point3D(0, 0, 0) fun Point3D.toMeta() = buildMeta { - VisualObject3D.x to x - VisualObject3D.y to y - VisualObject3D.z to z + VisualObject3D.x put x + VisualObject3D.y put y + VisualObject3D.z put z } \ No newline at end of file diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/transform/RemoveSingleChild.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/transform/RemoveSingleChild.kt index 1fc1e52b..f8003b2c 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/transform/RemoveSingleChild.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/transform/RemoveSingleChild.kt @@ -10,7 +10,9 @@ import hep.dataforge.vis.spatial.* internal fun mergeChild(parent: VisualGroup, child: VisualObject): VisualObject { return child.apply { - parent.properties?.let { config.update(it) } + config.update(parent.config) + + //parent.properties?.let { config.update(it) } if (this is VisualObject3D && parent is VisualObject3D) { position += parent.position diff --git a/dataforge-vis-spatial/src/commonTest/kotlin/hep/dataforge/vis/spatial/PropertyTest.kt b/dataforge-vis-spatial/src/commonTest/kotlin/hep/dataforge/vis/spatial/PropertyTest.kt new file mode 100644 index 00000000..dcece5bd --- /dev/null +++ b/dataforge-vis-spatial/src/commonTest/kotlin/hep/dataforge/vis/spatial/PropertyTest.kt @@ -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) + } +} \ No newline at end of file diff --git a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/editor/propertyEditor.kt b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/editor/propertyEditor.kt index f123ad4d..0aaaa1d9 100644 --- a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/editor/propertyEditor.kt +++ b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/editor/propertyEditor.kt @@ -5,13 +5,10 @@ import hep.dataforge.js.jsObject import hep.dataforge.meta.* import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.common.findStyle -import hep.dataforge.vis.spatial.Material3D.Companion.COLOR_KEY -import hep.dataforge.vis.spatial.Material3D.Companion.OPACITY_KEY +import hep.dataforge.vis.spatial.* +import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_COLOR_KEY +import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_OPACITY_KEY import hep.dataforge.vis.spatial.VisualObject3D.Companion.VISIBLE_KEY -import hep.dataforge.vis.spatial.color -import hep.dataforge.vis.spatial.opacity -import hep.dataforge.vis.spatial.prototype -import hep.dataforge.vis.spatial.visible import kotlinx.html.dom.append import kotlinx.html.js.div import kotlinx.html.js.h4 @@ -27,11 +24,17 @@ fun Element.propertyEditor(item: VisualObject?) { if (item != null) { append { card("Properties") { - val config = (item.properties ?: item.prototype?.properties) ?: EmptyMeta + val config: Meta = if (item is Proxy || item is Proxy.ProxyChild) { + item.prototype?.config ?: EmptyMeta + } else { + item.config + } val metaToEdit = config.builder().apply { VISIBLE_KEY to (item.visible ?: true) - COLOR_KEY to (item.color ?: "#ffffff") - OPACITY_KEY to (item.opacity ?: 1.0) + if (item is VisualObject3D) { + MATERIAL_COLOR_KEY to (item.color ?: "#ffffff") + MATERIAL_OPACITY_KEY to (item.opacity ?: 1.0) + } } val dMeta: dynamic = metaToEdit.toDynamic() val options: JSONEditorOptions = jsObject { diff --git a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/MeshThreeFactory.kt b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/MeshThreeFactory.kt index e1101ab6..7c3ead15 100644 --- a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/MeshThreeFactory.kt +++ b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/MeshThreeFactory.kt @@ -35,7 +35,7 @@ abstract class MeshThreeFactory( //JS sometimes tries to pass Geometry as BufferGeometry @Suppress("USELESS_IS_CHECK") if (geometry !is BufferGeometry) error("BufferGeometry expected") - val meshMeta: Meta = obj.properties[Material3D.MATERIAL_KEY]?.node ?: Meta.empty + //val meshMeta: Meta = obj.properties[Material3D.MATERIAL_KEY]?.node ?: Meta.empty val mesh = Mesh(geometry, MeshBasicMaterial()).apply { matrixAutoUpdate = false diff --git a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeMaterials.kt b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeMaterials.kt index 4ac3d4a8..edc760c7 100644 --- a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeMaterials.kt +++ b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeMaterials.kt @@ -11,7 +11,6 @@ import info.laht.threekt.materials.MeshBasicMaterial import info.laht.threekt.materials.MeshPhongMaterial import info.laht.threekt.math.Color import info.laht.threekt.objects.Mesh -import kotlin.math.max object ThreeMaterials { @@ -46,12 +45,6 @@ object ThreeMaterials { linewidth = meta["thickness"].double ?: 1.0 } } - - fun rgbToString(rgb: Int): String { - val string = rgb.toString(16).padStart(6, '0') - return "#" + string.substring(max(0, string.length - 6)) - } - } /** @@ -67,9 +60,9 @@ fun MetaItem<*>.color(): Color { } is MetaItem.NodeItem -> { Color( - node["red"]?.int ?: 0, - node["green"]?.int ?: 0, - node["blue"]?.int ?: 0 + node[Colors.RED_KEY]?.int ?: 0, + node[Colors.GREEN_KEY]?.int ?: 0, + node[Colors.BLUE_KEY]?.int ?: 0 ) } } @@ -99,11 +92,12 @@ fun MetaItem<*>.color(): Color { //fun Material3D?.jsLineMaterial(): Material = this?.config.jsLineMaterial() fun Mesh.updateMaterial(obj: VisualObject) { - val meta = obj.properties[Material3D.MATERIAL_KEY].node?:EmptyMeta + val meta = obj.getProperty(Material3D.MATERIAL_KEY).node?:EmptyMeta material = (material as? MeshBasicMaterial ?: MeshBasicMaterial()).apply { - color = meta["color"]?.color() ?: ThreeMaterials.DEFAULT_COLOR - opacity = meta["opacity"]?.double ?: 1.0 - transparent = meta["transparent"].boolean ?: (opacity < 1.0) + color = meta[Material3D.COLOR_KEY]?.color() ?: ThreeMaterials.DEFAULT_COLOR + opacity = meta[Material3D.OPACITY_KEY]?.double ?: 1.0 + transparent = opacity < 1.0 + wireframe = meta[Material3D.WIREFRAME_KEY].boolean?:false needsUpdate = true } } diff --git a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreePlugin.kt b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreePlugin.kt index ddcff805..b1356d3b 100644 --- a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreePlugin.kt +++ b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreePlugin.kt @@ -27,6 +27,7 @@ class ThreePlugin : AbstractPlugin() { objectFactories[Sphere::class] = ThreeSphereFactory objectFactories[ConeSegment::class] = ThreeCylinderFactory objectFactories[PolyLine::class] = ThreeLineFactory + objectFactories[Label3D::class] = ThreeTextFactory } @Suppress("UNCHECKED_CAST") diff --git a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeTextFactory.kt b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeTextFactory.kt new file mode 100644 index 00000000..8ab3e8bc --- /dev/null +++ b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeTextFactory.kt @@ -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 { + override val type: KClass 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 + } +} \ No newline at end of file diff --git a/dataforge-vis-spatial/src/jvmMain/kotlin/hep/dataforge/vis/spatial/demo/FXDemoApp.kt b/dataforge-vis-spatial/src/jvmMain/kotlin/hep/dataforge/vis/spatial/demo/FXDemoApp.kt deleted file mode 100644 index 489b536c..00000000 --- a/dataforge-vis-spatial/src/jvmMain/kotlin/hep/dataforge/vis/spatial/demo/FXDemoApp.kt +++ /dev/null @@ -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() -} \ No newline at end of file diff --git a/dataforge-vis-spatial/src/jvmMain/kotlin/hep/dataforge/vis/spatial/fx/FX3DPlugin.kt b/dataforge-vis-spatial/src/jvmMain/kotlin/hep/dataforge/vis/spatial/fx/FX3DPlugin.kt index 40f2a435..7c44d47f 100644 --- a/dataforge-vis-spatial/src/jvmMain/kotlin/hep/dataforge/vis/spatial/fx/FX3DPlugin.kt +++ b/dataforge-vis-spatial/src/jvmMain/kotlin/hep/dataforge/vis/spatial/fx/FX3DPlugin.kt @@ -2,13 +2,20 @@ package hep.dataforge.vis.spatial.fx import hep.dataforge.context.* import hep.dataforge.meta.Meta +import hep.dataforge.meta.boolean import hep.dataforge.meta.get import hep.dataforge.provider.Type import hep.dataforge.vis.spatial.* +import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_KEY +import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_WIREFRAME_KEY import hep.dataforge.vis.spatial.fx.FX3DFactory.Companion.TYPE import javafx.scene.Group import javafx.scene.Node +import javafx.scene.shape.CullFace +import javafx.scene.shape.DrawMode import javafx.scene.shape.Shape3D +import javafx.scene.text.Font +import javafx.scene.text.Text import javafx.scene.transform.Rotate import org.fxyz3d.shapes.composites.PolyLine3D import org.fxyz3d.shapes.primitives.CuboidMesh @@ -56,11 +63,18 @@ class FX3DPlugin : AbstractPlugin() { } else { FXShapeFactory(obj, binding) } + is Label3D -> Text(obj.text).apply { + font = Font.font(obj.fontFamily, obj.fontSize) + x = -layoutBounds.width / 2 + y = layoutBounds.height / 2 + } is PolyLine -> PolyLine3D( obj.points.map { it.point }, obj.thickness.toFloat(), - obj.material?.get("color")?.color() - ) + obj.getProperty(Material3D.MATERIAL_COLOR_KEY)?.color() + ).apply { + this.meshView.cullFace = CullFace.FRONT + } else -> { //find specialized factory for this type if it is present val factory: FX3DFactory? = findObjectFactory(obj::class) @@ -79,15 +93,15 @@ class FX3DPlugin : AbstractPlugin() { scaleZProperty().bind(binding[VisualObject3D.zScale].float(obj.scaleZ.toFloat())) val rotateX = Rotate(0.0, Rotate.X_AXIS).apply { - angleProperty().bind(binding[VisualObject3D.xRotation].float(obj.rotationX.toFloat())) + angleProperty().bind(binding[VisualObject3D.xRotation].float(obj.rotationX.toFloat()).multiply(180.0 / PI)) } val rotateY = Rotate(0.0, Rotate.Y_AXIS).apply { - angleProperty().bind(binding[VisualObject3D.yRotation].float(obj.rotationY.toFloat())) + angleProperty().bind(binding[VisualObject3D.yRotation].float(obj.rotationY.toFloat()).multiply(180.0 / PI)) } val rotateZ = Rotate(0.0, Rotate.Z_AXIS).apply { - angleProperty().bind(binding[VisualObject3D.zRotation].float(obj.rotationZ.toFloat())) + angleProperty().bind(binding[VisualObject3D.zRotation].float(obj.rotationZ.toFloat()).multiply(180.0 / PI)) } when (obj.rotationOrder) { @@ -100,9 +114,17 @@ class FX3DPlugin : AbstractPlugin() { } if (this is Shape3D) { - materialProperty().bind(binding[Material3D.MATERIAL_KEY].transform { + materialProperty().bind(binding[MATERIAL_KEY].transform { it.material() }) + + drawModeProperty().bind(binding[MATERIAL_WIREFRAME_KEY].transform { + if (it.boolean == true) { + DrawMode.LINE + } else { + DrawMode.FILL + } + }) } } } diff --git a/dataforge-vis-spatial/src/jvmMain/kotlin/hep/dataforge/vis/spatial/fx/FXCanvas3D.kt b/dataforge-vis-spatial/src/jvmMain/kotlin/hep/dataforge/vis/spatial/fx/FXCanvas3D.kt index 7c7663e0..a8f38b73 100644 --- a/dataforge-vis-spatial/src/jvmMain/kotlin/hep/dataforge/vis/spatial/fx/FXCanvas3D.kt +++ b/dataforge-vis-spatial/src/jvmMain/kotlin/hep/dataforge/vis/spatial/fx/FXCanvas3D.kt @@ -33,13 +33,15 @@ class FXCanvas3D(val plugin: FX3DPlugin, meta: Meta = EmptyMeta) : it.setRadius(meta["axis.width"].double ?: LINE_WIDTH) it.isVisible = meta["axis.visible"].boolean ?: (meta["axis"] != null) world.add(it) - } + val light = AmbientLight() + private val camera = PerspectiveCamera().apply { nearClip = CAMERA_NEAR_CLIP farClip = CAMERA_FAR_CLIP translateZ = CAMERA_INITIAL_DISTANCE + this.add(light) } val cameraTransform = CameraTransformer().also { @@ -60,20 +62,27 @@ class FXCanvas3D(val plugin: FX3DPlugin, meta: Meta = EmptyMeta) : val rotationZProperty get() = cameraTransform.rz.angleProperty() var angleZ by rotationZProperty + private val canvas = SubScene( + Group(world, cameraTransform).apply { DepthTest.ENABLE }, + 400.0, + 400.0, + true, + SceneAntialiasing.BALANCED + ).also { scene -> + scene.fill = Color.GREY + scene.camera = camera + //id = "canvas" + handleKeyboard(scene) + handleMouse(scene) + } + override val root = borderpane { - center = SubScene( - Group(world, cameraTransform).apply { DepthTest.ENABLE }, - 1024.0, - 768.0, - true, - SceneAntialiasing.BALANCED - ).also { scene -> - scene.fill = Color.GREY - scene.camera = camera - id = "canvas" - handleKeyboard(scene) - handleMouse(scene) - } + center = canvas + } + + init { + canvas.widthProperty().bind(root.widthProperty()) + canvas.heightProperty().bind(root.heightProperty()) } @@ -170,6 +179,6 @@ class FXCanvas3D(val plugin: FX3DPlugin, meta: Meta = EmptyMeta) : private const val ROTATION_SPEED = 2.0 private const val TRACK_SPEED = 6.0 private const val RESIZE_SPEED = 50.0 - private const val LINE_WIDTH = 3.0 + private const val LINE_WIDTH = 1.0 } } \ No newline at end of file diff --git a/dataforge-vis-spatial/src/jvmMain/kotlin/hep/dataforge/vis/spatial/fx/FXCompositeFactory.kt b/dataforge-vis-spatial/src/jvmMain/kotlin/hep/dataforge/vis/spatial/fx/FXCompositeFactory.kt index 1b11f2c0..a4af60f6 100644 --- a/dataforge-vis-spatial/src/jvmMain/kotlin/hep/dataforge/vis/spatial/fx/FXCompositeFactory.kt +++ b/dataforge-vis-spatial/src/jvmMain/kotlin/hep/dataforge/vis/spatial/fx/FXCompositeFactory.kt @@ -1,25 +1,57 @@ package hep.dataforge.vis.spatial.fx import eu.mihosoft.jcsg.CSG +import eu.mihosoft.jcsg.Polygon +import eu.mihosoft.vvecmath.Vector3d import hep.dataforge.vis.spatial.Composite import hep.dataforge.vis.spatial.CompositeType import javafx.scene.Group import javafx.scene.Node import javafx.scene.shape.MeshView -import org.fxyz3d.utils.MeshUtils +import javafx.scene.shape.TriangleMesh +import javafx.scene.shape.VertexFormat +import java.util.* +import kotlin.collections.HashMap import kotlin.reflect.KClass -class FXCompositeFactory(val plugin: FX3DPlugin) : - FX3DFactory { +private fun MeshView.toCSG(): CSG { + val mesh = this.mesh as TriangleMesh + if (mesh.vertexFormat != VertexFormat.POINT_TEXCOORD) error("Not POINT_TEXCOORD") + val polygons: MutableList = ArrayList() + val faces = mesh.faces + val points = mesh.points + + val vectorCache = HashMap() + 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 { override val type: KClass get() = Composite::class override fun invoke(obj: Composite, binding: VisualObjectFXBinding): Node { val first = plugin.buildNode(obj.first) as? MeshView ?: error("Can't build node") val second = plugin.buildNode(obj.second) as? MeshView ?: error("Can't build node") - val firstCSG = MeshUtils.mesh2CSG(first) - val secondCSG = MeshUtils.mesh2CSG(second) - val resultCSG = when(obj.compositeType){ + val firstCSG = first.toCSG() + val secondCSG = second.toCSG() + val resultCSG = when (obj.compositeType) { CompositeType.UNION -> firstCSG.union(secondCSG) CompositeType.INTERSECT -> firstCSG.intersect(secondCSG) CompositeType.SUBTRACT -> firstCSG.difference(secondCSG) @@ -28,9 +60,9 @@ class FXCompositeFactory(val plugin: FX3DPlugin) : } } -internal fun CSG.toNode(): Node{ +internal fun CSG.toNode(): Node { val meshes = toJavaFXMesh().asMeshViews - return if(meshes.size == 1){ + return if (meshes.size == 1) { meshes.first() } else { Group(meshes.map { it }) diff --git a/dataforge-vis-spatial/src/jvmMain/kotlin/hep/dataforge/vis/spatial/fx/FXMaterials.kt b/dataforge-vis-spatial/src/jvmMain/kotlin/hep/dataforge/vis/spatial/fx/FXMaterials.kt index 2237a46b..af8cb2ee 100644 --- a/dataforge-vis-spatial/src/jvmMain/kotlin/hep/dataforge/vis/spatial/fx/FXMaterials.kt +++ b/dataforge-vis-spatial/src/jvmMain/kotlin/hep/dataforge/vis/spatial/fx/FXMaterials.kt @@ -5,6 +5,8 @@ import hep.dataforge.meta.double import hep.dataforge.meta.get import hep.dataforge.meta.int import hep.dataforge.values.ValueType +import hep.dataforge.vis.common.Colors +import hep.dataforge.vis.spatial.Material3D import javafx.scene.paint.Color import javafx.scene.paint.Material import javafx.scene.paint.PhongMaterial @@ -39,16 +41,16 @@ fun MetaItem<*>.color(opacity: Double = 1.0): Color { val red = int and 0x00ff0000 shr 16 val green = int and 0x0000ff00 shr 8 val blue = int and 0x000000ff - Color.rgb(red, green, blue) + Color.rgb(red, green, blue, opacity) } else { Color.web(this.value.string) } is MetaItem.NodeItem -> { Color.rgb( - node["red"]?.int ?: 0, - node["green"]?.int ?: 0, - node["blue"]?.int ?: 0, - node["opacity"]?.double ?: opacity + node[Colors.RED_KEY]?.int ?: 0, + node[Colors.GREEN_KEY]?.int ?: 0, + node[Colors.BLUE_KEY]?.int ?: 0, + node[Material3D.OPACITY_KEY]?.double ?: opacity ) } } @@ -62,9 +64,9 @@ fun MetaItem<*>?.material(): Material { null -> FXMaterials.GREY is MetaItem.ValueItem -> PhongMaterial(color()) is MetaItem.NodeItem -> PhongMaterial().apply { - val opacity = node["opacity"].double ?: 1.0 - diffuseColor = node["color"]?.color(opacity) ?: Color.DARKGREY - specularColor = node["specularColor"]?.color(opacity) ?: Color.WHITE + val opacity = node[Material3D.OPACITY_KEY].double ?: 1.0 + diffuseColor = node[Material3D.COLOR_KEY]?.color(opacity) ?: Color.DARKGREY + specularColor = node[Material3D.SPECULAR_COLOR]?.color(opacity) ?: Color.WHITE } } } diff --git a/dataforge-vis-spatial/src/jvmMain/kotlin/hep/dataforge/vis/spatial/fx/FXShapeFactory.kt b/dataforge-vis-spatial/src/jvmMain/kotlin/hep/dataforge/vis/spatial/fx/FXShapeFactory.kt index cd851fe6..0dd58c45 100644 --- a/dataforge-vis-spatial/src/jvmMain/kotlin/hep/dataforge/vis/spatial/fx/FXShapeFactory.kt +++ b/dataforge-vis-spatial/src/jvmMain/kotlin/hep/dataforge/vis/spatial/fx/FXShapeFactory.kt @@ -7,6 +7,7 @@ import hep.dataforge.vis.spatial.Shape import javafx.scene.shape.Mesh import javafx.scene.shape.MeshView import javafx.scene.shape.TriangleMesh +import javafx.scene.shape.VertexFormat import org.fxyz3d.geometry.Face3 import kotlin.reflect.KClass @@ -42,13 +43,16 @@ private class FXGeometryBuilder : GeometryBuilder { } override fun build(): Mesh { - val mesh = TriangleMesh() + val mesh = TriangleMesh(VertexFormat.POINT_TEXCOORD) vertices.forEach { //TODO optimize copy mesh.points.addAll(it.x.toFloat(), it.y.toFloat(), it.z.toFloat()) } + + mesh.texCoords.addAll(0f, 0f) + faces.forEach { - mesh.faces.addAll(it.p0, it.p1, it.p2) + mesh.faces.addAll(it.p0, 0, it.p1, 0, it.p2, 0) } return mesh } diff --git a/dataforge-vis-spatial/src/jvmMain/kotlin/hep/dataforge/vis/spatial/fx/VisualObjectFXBinding.kt b/dataforge-vis-spatial/src/jvmMain/kotlin/hep/dataforge/vis/spatial/fx/VisualObjectFXBinding.kt index bc772892..faa7b843 100644 --- a/dataforge-vis-spatial/src/jvmMain/kotlin/hep/dataforge/vis/spatial/fx/VisualObjectFXBinding.kt +++ b/dataforge-vis-spatial/src/jvmMain/kotlin/hep/dataforge/vis/spatial/fx/VisualObjectFXBinding.kt @@ -2,9 +2,10 @@ package hep.dataforge.vis.spatial.fx import hep.dataforge.meta.* import hep.dataforge.names.Name -import hep.dataforge.names.isEmpty +import hep.dataforge.names.startsWith import hep.dataforge.names.toName import hep.dataforge.vis.common.VisualObject +import javafx.application.Platform import javafx.beans.binding.ObjectBinding import tornadofx.* @@ -16,12 +17,17 @@ class VisualObjectFXBinding(val obj: VisualObject) { init { obj.onPropertyChange(this) { name, _, _ -> - var currentName = name - while(!currentName.isEmpty()) { - //recursively update all upper level bindings - bindings[currentName]?.invalidate() - currentName = currentName.cutLast() + bindings.filter { it.key.startsWith(name) }.forEach { entry -> + Platform.runLater { + entry.value.invalidate() + } } +// var currentName = name +// while (!currentName.isEmpty()) { +// //recursively update all upper level bindings +// bindings[currentName]?.invalidate() +// currentName = currentName.cutLast() +// } } } @@ -46,9 +52,9 @@ fun ObjectBinding?>.long() = objectBinding { it.long } fun ObjectBinding?>.node() = objectBinding { it.node } fun ObjectBinding?>.string(default: String) = stringBinding { it.string ?: default } -fun ObjectBinding?>.double(default: Double) = objectBinding { it.double ?: default } -fun ObjectBinding?>.float(default: Float) = objectBinding { it.float ?: default } -fun ObjectBinding?>.int(default: Int) = objectBinding { it.int ?: default } -fun ObjectBinding?>.long(default: Long) = objectBinding { it.long ?:default } +fun ObjectBinding?>.double(default: Double) = doubleBinding { it.double ?: default } +fun ObjectBinding?>.float(default: Float) = floatBinding { it.float ?: default } +fun ObjectBinding?>.int(default: Int) = integerBinding { it.int ?: default } +fun ObjectBinding?>.long(default: Long) = longBinding { it.long ?: default } fun ObjectBinding?>.transform(transform: (MetaItem<*>) -> T) = objectBinding { it?.let(transform) } diff --git a/settings.gradle.kts b/settings.gradle.kts index f3511af6..9827167d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -28,10 +28,9 @@ rootProject.name = "dataforge-vis" include( ":dataforge-vis-common", ":wrappers", - ":dataforge-vis-fx", ":dataforge-vis-spatial", ":dataforge-vis-spatial-gdml", - ":spatial-js-demo" + ":spatial-demo" ) //if(file("../dataforge-core").exists()) { diff --git a/spatial-demo/build.gradle.kts b/spatial-demo/build.gradle.kts new file mode 100644 index 00000000..46d63633 --- /dev/null +++ b/spatial-demo/build.gradle.kts @@ -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 { + modules("javafx.controls") +} \ No newline at end of file diff --git a/spatial-demo/src/commonMain/kotlin/hep/dataforge/vis/spatial/demo/demo.kt b/spatial-demo/src/commonMain/kotlin/hep/dataforge/vis/spatial/demo/demo.kt new file mode 100644 index 00000000..09728066 --- /dev/null +++ b/spatial-demo/src/commonMain/kotlin/hep/dataforge/vis/spatial/demo/demo.kt @@ -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) + } + } +} \ No newline at end of file diff --git a/spatial-demo/src/jsMain/kotlin/hep/dataforge/vis/spatial/demo/ThreeDemoApp.kt b/spatial-demo/src/jsMain/kotlin/hep/dataforge/vis/spatial/demo/ThreeDemoApp.kt new file mode 100644 index 00000000..e55a3ccb --- /dev/null +++ b/spatial-demo/src/jsMain/kotlin/hep/dataforge/vis/spatial/demo/ThreeDemoApp.kt @@ -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) { + + 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()//mapOf("lines" put presenter.dispose()) +} + +fun main() { + startApplication(::ThreeDemoApp) +} \ No newline at end of file diff --git a/spatial-js-demo/src/main/kotlin/hep/dataforge/vis/spatial/demo/ThreeDemoGrid.kt b/spatial-demo/src/jsMain/kotlin/hep/dataforge/vis/spatial/demo/ThreeDemoGrid.kt similarity index 55% rename from spatial-js-demo/src/main/kotlin/hep/dataforge/vis/spatial/demo/ThreeDemoGrid.kt rename to spatial-demo/src/jsMain/kotlin/hep/dataforge/vis/spatial/demo/ThreeDemoGrid.kt index 94e9b709..8d1aadc4 100644 --- a/spatial-js-demo/src/main/kotlin/hep/dataforge/vis/spatial/demo/ThreeDemoGrid.kt +++ b/spatial-demo/src/jsMain/kotlin/hep/dataforge/vis/spatial/demo/ThreeDemoGrid.kt @@ -1,20 +1,13 @@ package hep.dataforge.vis.spatial.demo -import hep.dataforge.context.AbstractPlugin -import hep.dataforge.context.Context -import hep.dataforge.context.PluginFactory -import hep.dataforge.context.PluginTag +import hep.dataforge.context.Global import hep.dataforge.meta.Meta -import hep.dataforge.meta.buildMeta import hep.dataforge.meta.get import hep.dataforge.meta.string import hep.dataforge.names.Name -import hep.dataforge.names.toName import hep.dataforge.output.OutputManager import hep.dataforge.output.Renderer import hep.dataforge.vis.common.VisualObject -import hep.dataforge.vis.spatial.VisualGroup3D -import hep.dataforge.vis.spatial.render import hep.dataforge.vis.spatial.three.ThreeCanvas import hep.dataforge.vis.spatial.three.ThreePlugin import hep.dataforge.vis.spatial.three.output @@ -25,31 +18,25 @@ import kotlinx.html.hr import kotlinx.html.id import kotlinx.html.js.div import kotlinx.html.span +import org.w3c.dom.Element import kotlin.browser.document import kotlin.dom.clear import kotlin.reflect.KClass -class ThreeDemoGrid(meta: Meta) : AbstractPlugin(meta), OutputManager { - override val tag: PluginTag get() = Companion.tag +class ThreeDemoGrid(element: Element, meta: Meta = Meta.empty) : OutputManager { private val gridRoot = document.create.div("row") private val outputs: MutableMap = HashMap() - init { - require(ThreePlugin) - } + private val three = Global.plugins.fetch(ThreePlugin) - override fun attach(context: Context) { - super.attach(context) - val elementId = meta["elementID"].string ?: "canvas" - val element = document.getElementById(elementId) ?: error("Element with id $elementId not found on page") + init { element.clear() element.append(gridRoot) } @Suppress("UNCHECKED_CAST") override fun get(type: KClass, name: Name, stage: Name, meta: Meta): Renderer { - val three = context.plugins.get()!! return outputs.getOrPut(name) { if (type != VisualObject::class) error("Supports only DisplayObject") @@ -63,7 +50,7 @@ class ThreeDemoGrid(meta: Meta) : AbstractPlugin(meta), OutputManager { gridRoot.append { span("border") { div("col-6") { - div { id = "output-$name" }.also{ + div { id = "output-$name" }.also { output.attach(it) } hr() @@ -75,20 +62,5 @@ class ThreeDemoGrid(meta: Meta) : AbstractPlugin(meta), OutputManager { output } as Renderer } - - companion object : PluginFactory { - override val tag: PluginTag = PluginTag(group = "hep.dataforge", name = "vis.js.spatial.demo") - - override val type: KClass = 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) -} \ No newline at end of file diff --git a/spatial-js-demo/src/main/kotlin/hep/dataforge/vis/spatial/demo/VariableBox.kt b/spatial-demo/src/jsMain/kotlin/hep/dataforge/vis/spatial/demo/VariableBox.kt similarity index 100% rename from spatial-js-demo/src/main/kotlin/hep/dataforge/vis/spatial/demo/VariableBox.kt rename to spatial-demo/src/jsMain/kotlin/hep/dataforge/vis/spatial/demo/VariableBox.kt diff --git a/spatial-js-demo/src/main/web/index.html b/spatial-demo/src/jsMain/web/index.html similarity index 100% rename from spatial-js-demo/src/main/web/index.html rename to spatial-demo/src/jsMain/web/index.html diff --git a/spatial-demo/src/jvmMain/kotlin/hep/dataforge/vis/spatial/demo/FXDemoApp.kt b/spatial-demo/src/jvmMain/kotlin/hep/dataforge/vis/spatial/demo/FXDemoApp.kt new file mode 100644 index 00000000..3fbaff04 --- /dev/null +++ b/spatial-demo/src/jvmMain/kotlin/hep/dataforge/vis/spatial/demo/FXDemoApp.kt @@ -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() +} \ No newline at end of file diff --git a/dataforge-vis-spatial/src/jvmMain/kotlin/hep/dataforge/vis/spatial/demo/FXDemoGrid.kt b/spatial-demo/src/jvmMain/kotlin/hep/dataforge/vis/spatial/demo/FXDemoGrid.kt similarity index 86% rename from dataforge-vis-spatial/src/jvmMain/kotlin/hep/dataforge/vis/spatial/demo/FXDemoGrid.kt rename to spatial-demo/src/jvmMain/kotlin/hep/dataforge/vis/spatial/demo/FXDemoGrid.kt index e6b23039..21d98b8e 100644 --- a/dataforge-vis-spatial/src/jvmMain/kotlin/hep/dataforge/vis/spatial/demo/FXDemoGrid.kt +++ b/spatial-demo/src/jvmMain/kotlin/hep/dataforge/vis/spatial/demo/FXDemoGrid.kt @@ -47,12 +47,4 @@ class FXDemoGrid : View(), OutputManager { } as Renderer } -} - -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) } \ No newline at end of file diff --git a/spatial-js-demo/build.gradle.kts b/spatial-js-demo/build.gradle.kts deleted file mode 100644 index 19b614fd..00000000 --- a/spatial-js-demo/build.gradle.kts +++ /dev/null @@ -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 -// } -// } -// } -//} \ No newline at end of file diff --git a/spatial-js-demo/src/main/kotlin/hep/dataforge/vis/spatial/demo/ThreeDemoApp.kt b/spatial-js-demo/src/main/kotlin/hep/dataforge/vis/spatial/demo/ThreeDemoApp.kt deleted file mode 100644 index 01e6f690..00000000 --- a/spatial-js-demo/src/main/kotlin/hep/dataforge/vis/spatial/demo/ThreeDemoApp.kt +++ /dev/null @@ -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) { - - //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()//mapOf("lines" put presenter.dispose()) -} - -fun main() { - startApplication(::ThreeDemoApp) -} \ No newline at end of file