Large API update

This commit is contained in:
Alexander Nozik 2019-12-22 20:56:19 +03:00
parent 0164534004
commit 8189012b70
51 changed files with 894 additions and 906 deletions

View File

@ -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

View File

@ -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")

View File

@ -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")

View File

@ -1,8 +1,10 @@
package hep.dataforge.vis.common
import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaItem
import hep.dataforge.names.*
import hep.dataforge.names.Name
import hep.dataforge.names.NameToken
import hep.dataforge.names.asName
import hep.dataforge.names.isEmpty
import kotlinx.serialization.Transient
@ -16,46 +18,7 @@ abstract class AbstractVisualGroup : AbstractVisualObject(), MutableVisualGroup
/**
* A map of top level named children
*/
abstract override val children: Map<NameToken, VisualObject> //get() = _children
/**
* Styles, defined in this group. A style could be defined but not applied
* TODO replace by custom object with get/set functionality
*/
protected abstract val styleSheet: MutableMap<Name, Meta>
override fun getStyle(name: Name): Meta? = styleSheet[name]
override fun addStyle(name: Name, meta: Meta, apply: Boolean) {
fun VisualObject.applyStyle(name: Name, meta: Meta) {
if (styles.contains(name)) {
//full update
//TODO do a fine grained update
if (this is AbstractVisualObject) {
styleChanged()
} else {
propertyChanged(EmptyName)
}
}
if (this is VisualGroup) {
this.children.forEach { (_, child) ->
child.applyStyle(name, meta)
}
}
}
styleSheet[name] = meta
if (apply) {
applyStyle(name, meta)
}
}
// init {
// //Do after deserialization
// children.values.forEach {
// it.parent = this
// }
// }
abstract override val children: Map<NameToken, VisualObject>
override fun propertyChanged(name: Name, before: MetaItem<*>?, after: MetaItem<*>?) {
super.propertyChanged(name, before, after)

View File

@ -1,9 +1,8 @@
package hep.dataforge.vis.common
import hep.dataforge.meta.*
import hep.dataforge.names.EmptyName
import hep.dataforge.names.Name
import hep.dataforge.names.toName
import hep.dataforge.names.asName
import hep.dataforge.vis.common.VisualObject.Companion.STYLE_KEY
import kotlinx.serialization.Transient
@ -17,15 +16,25 @@ abstract class AbstractVisualObject : VisualObject {
@Transient
override var parent: VisualObject? = null
abstract override var properties: Config?
protected abstract var properties: Config?
override var styles: List<Name>
get() = properties?.get(STYLE_KEY).stringList.map(String::toName)
override var styles: List<String>
get() = properties?.get(STYLE_KEY).stringList
set(value) {
setProperty(STYLE_KEY, value.map { it.toString() })
styleChanged()
//val allStyles = (field + value).distinct()
setProperty(STYLE_KEY, value)
updateStyles(value)
}
protected fun updateStyles(names: List<String>) {
names.mapNotNull { findStyle(it) }.asSequence()
.flatMap { it.items.asSequence() }
.distinctBy { it.key }
.forEach {
propertyChanged(it.key.asName(), null, it.value)
}
}
/**
* The config is initialized and assigned on-demand.
* To avoid unnecessary allocations, one should access [properties] via [getProperty] instead.
@ -67,14 +76,6 @@ abstract class AbstractVisualObject : VisualObject {
}
/**
* Helper to reset style cache
*/
protected fun styleChanged() {
styleCache = null
propertyChanged(EmptyName)
}
override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? {
return if (inherit) {
properties?.get(name) ?: mergedStyles[name] ?: parent?.getProperty(name, inherit)
@ -84,10 +85,10 @@ abstract class AbstractVisualObject : VisualObject {
}
}
fun VisualObject.findStyle(styleName: Name): Meta? {
if (this is VisualGroup) {
val style = getStyle(styleName)
if (style != null) return style
}
return parent?.findStyle(styleName)
}
//fun VisualObject.findStyle(styleName: Name): Meta? {
// if (this is VisualGroup) {
// val style = resolveStyle(styleName)
// if (style != null) return style
// }
// return parent?.findStyle(styleName)
//}

View File

@ -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()
}
}

View File

@ -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) }
}
}

View File

@ -1,6 +1,8 @@
package hep.dataforge.vis.common
import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaBuilder
import hep.dataforge.meta.buildMeta
import hep.dataforge.meta.seal
import hep.dataforge.names.*
import hep.dataforge.provider.Provider
@ -12,6 +14,8 @@ interface VisualGroup : Provider, Iterable<VisualObject>, VisualObject {
override val defaultTarget: String get() = VisualObject.TYPE
val styleSheet: StyleSheet
override fun provideTop(target: String): Map<Name, Any> =
when (target) {
VisualObject.TYPE -> children.flatMap { (key, value) ->
@ -22,7 +26,7 @@ interface VisualGroup : Provider, Iterable<VisualObject>, VisualObject {
}
res.entries
}.associate { it.toPair() }
//TODO add styles
STYLE_TARGET -> styleSheet.items.mapKeys { it.key.toName() }
else -> emptyMap()
}
@ -32,17 +36,6 @@ interface VisualGroup : Provider, Iterable<VisualObject>, VisualObject {
*/
override fun iterator(): Iterator<VisualObject> = children.values.iterator()
/**
* Resolve style by its name
* TODO change to Config?
*/
fun getStyle(name: Name): Meta?
/**
* Add or replace style with given name
*/
fun addStyle(name: Name, meta: Meta, apply: Boolean = true)
operator fun get(name: Name): VisualObject? {
return when {
name.isEmpty() -> this
@ -50,8 +43,30 @@ interface VisualGroup : Provider, Iterable<VisualObject>, VisualObject {
else -> (children[name.first()!!] as? VisualGroup)?.get(name.cutFirst())
}
}
/**
* A fix for serialization bug that writes all proper parents inside the tree after deserialization
*/
fun attachChildren(){
styleSheet.owner = this
this.children.values.forEach {
it.parent = this
(it as? VisualGroup)?.attachChildren()
}
}
companion object {
const val STYLE_TARGET = "style"
}
}
fun VisualGroup.updateStyle(key: String, builder: MetaBuilder.() -> Unit) {
val newStyle = styleSheet[key]?.let { buildMeta(it, builder) } ?: buildMeta(builder)
styleSheet[key] = newStyle.seal()
}
data class StyleRef(val group: VisualGroup, val styleName: Name)
val VisualGroup.isEmpty: Boolean get() = this.children.isEmpty()
interface MutableVisualGroup : VisualGroup {

View File

@ -1,9 +1,6 @@
package hep.dataforge.vis.common
import hep.dataforge.meta.Config
import hep.dataforge.meta.Configurable
import hep.dataforge.meta.Laminate
import hep.dataforge.meta.MetaItem
import hep.dataforge.meta.*
import hep.dataforge.names.Name
import hep.dataforge.names.asName
import hep.dataforge.names.toName
@ -26,11 +23,6 @@ interface VisualObject : Configurable {
@Transient
var parent: VisualObject?
/**
* Direct properties access
*/
val properties: Config?
/**
* Set property for this object
*/
@ -42,9 +34,11 @@ interface VisualObject : Configurable {
fun getProperty(name: Name, inherit: Boolean = true): MetaItem<*>?
/**
* Manually trigger property changed event. If [name] is empty, notify that the whole object is changed
* Trigger property invalidation event. If [name] is empty, notify that the whole object is changed
*/
fun propertyChanged(name: Name, before: MetaItem<*>? = null, after: MetaItem<*>? = null): Unit
fun propertyChanged(name: Name, before: MetaItem<*>?, after: MetaItem<*>?): Unit
fun propertyInvalidated(name: Name) = propertyChanged(name, null, null)
/**
* Add listener triggering on property change
@ -57,11 +51,9 @@ interface VisualObject : Configurable {
fun removeChangeListener(owner: Any?)
/**
* List of names of styles applied to this object. Order matters.
* List of names of styles applied to this object. Order matters. Not inherited
*/
var styles: List<Name>
fun findAllStyles(): Laminate = Laminate(styles.distinct().mapNotNull(::findStyle))
var styles: List<String>
companion object {
const val TYPE = "visual"
@ -69,12 +61,42 @@ interface VisualObject : Configurable {
//const val META_KEY = "@meta"
//const val TAGS_KEY = "@tags"
}
}
fun VisualObject.getProperty(key: String, inherit: Boolean = true): MetaItem<*>? = getProperty(key.toName(), inherit)
fun VisualObject.setProperty(key: String, value: Any?) = setProperty(key.toName(), value)
fun VisualObject.applyStyle(name: String) {
styles = styles + name.toName()
/**
* Add style name to the list of styles to be resolved later. The style with given name does not necessary exist at the moment.
*/
fun VisualObject.useStyle(name: String) {
styles = styles + name
}
//private tailrec fun VisualObject.topGroup(): VisualGroup? {
// val parent = this.parent
// return if (parent == null) {
// this as? VisualGroup
// }
// else {
// parent.topGroup()
// }
//}
//
///**
// * Add or update given style on a top-most reachable parent group and apply it to this object
// */
//fun VisualObject.useStyle(name: String, builder: MetaBuilder.() -> Unit) {
// val styleName = name.toName()
// topGroup()?.updateStyle(styleName, builder) ?: error("Can't find parent group for $this")
// useStyle(styleName)
//}
tailrec fun VisualObject.findStyle(name: String): Meta? =
(this as? VisualGroup)?.styleSheet?.get(name) ?: parent?.findStyle(name)
fun VisualObject.findAllStyles(): Laminate = Laminate(styles.mapNotNull(::findStyle))

View File

@ -1,32 +1,20 @@
import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpack
plugins {
id("scientifik.mpp")
}
scientifik{
withSerialization()
}
kotlin {
sourceSets {
val commonMain by getting {
dependencies {
api(project(":dataforge-vis-spatial"))
api("scientifik:gdml:0.1.3")
}
}
val jsMain by getting {
dependencies {
api(project(":dataforge-vis-spatial"))
//api("kotlin.js.externals:kotlin-js-jquery:3.2.0-0")
api("scientifik:gdml:0.1.4")
}
}
}
}
tasks{
val jsBrowserWebpack by getting(KotlinWebpack::class) {
sourceMaps = false
}
}
//tasks{
// val jsBrowserWebpack by getting(KotlinWebpack::class) {
// sourceMaps = false
// }
//}

View File

@ -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)

View File

@ -245,3 +245,10 @@ fun GDML.toVisual(block: GDMLTransformer.() -> Unit = {}): VisualGroup3D {
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))
}

View File

@ -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)

View File

@ -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()))
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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 {

View File

@ -1,4 +1,5 @@
@file:UseSerializers(Point3DSerializer::class)
package hep.dataforge.vis.spatial
import hep.dataforge.io.serialization.ConfigSerializer
@ -43,10 +44,9 @@ inline fun VisualGroup3D.composite(
val children = group.filterIsInstance<VisualObject3D>()
if (children.size != 2) error("Composite requires exactly two children")
return Composite(type, children[0], children[1]).also {
if (group.properties != null) {
it.config.update(group.config)
it.material = group.material
}
it.config.update(group.config)
//it.material = group.material
it.position = group.position
it.rotation = group.rotation
it.scale = group.scale

View File

@ -9,7 +9,8 @@ import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers
@Serializable
class Text3D(var text: String, var fontSize: Int) : AbstractVisualObject(), VisualObject3D {
class Label3D(var text: String, var fontSize: Double, var fontFamily: String) : AbstractVisualObject(),
VisualObject3D {
@Serializable(ConfigSerializer::class)
override var properties: Config? = null
@ -19,5 +20,11 @@ class Text3D(var text: String, var fontSize: Int) : AbstractVisualObject(), Visu
}
fun VisualGroup3D.text(text: String, fontSize: Int, name: String = "", action: Text3D.() -> Unit = {}) =
Text3D(text, fontSize).apply(action).also { set(name, it) }
fun VisualGroup3D.label(
text: String,
fontSize: Number = 20,
fontFamily: String = "Arial",
name: String = "",
action: Label3D.() -> Unit = {}
) =
Label3D(text, fontSize.toDouble(), fontFamily).apply(action).also { set(name, it) }

View File

@ -3,63 +3,72 @@ package hep.dataforge.vis.spatial
import hep.dataforge.meta.*
import hep.dataforge.names.asName
import hep.dataforge.names.plus
import hep.dataforge.vis.common.VisualObject
import hep.dataforge.vis.spatial.Material3D.Companion.COLOR_KEY
import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_KEY
import hep.dataforge.vis.spatial.Material3D.Companion.OPACITY_KEY
import hep.dataforge.vis.common.Colors
import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_COLOR_KEY
import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_OPACITY_KEY
class Material3D(override val config: Config) : Specific {
var color by string()
var color by string(key = COLOR_KEY)
var opacity by float(1f)
var specularColor by string()
var opacity by float(1f, key = OPACITY_KEY)
var wireframe by boolean(false, WIREFRAME_KEY)
companion object : Specification<Material3D> {
override fun wrap(config: Config): Material3D = Material3D(config)
val MATERIAL_KEY = "material".asName()
val COLOR_KEY = MATERIAL_KEY + "color"
val SPECULAR_COLOR = MATERIAL_KEY + "specularColor"
val OPACITY_KEY = MATERIAL_KEY + "opacity"
internal val COLOR_KEY = "color".asName()
val MATERIAL_COLOR_KEY = MATERIAL_KEY + COLOR_KEY
val SPECULAR_COLOR ="specularColor".asName()
internal val OPACITY_KEY = "opacity".asName()
val MATERIAL_OPACITY_KEY = MATERIAL_KEY + OPACITY_KEY
internal val WIREFRAME_KEY = "wireframe".asName()
val MATERIAL_WIREFRAME_KEY = MATERIAL_KEY + WIREFRAME_KEY
}
}
fun VisualObject.color(rgb: String) {
setProperty(COLOR_KEY, rgb)
fun VisualObject3D.color(rgb: String) {
setProperty(MATERIAL_COLOR_KEY, rgb)
}
fun VisualObject.color(rgb: Int) {
setProperty(COLOR_KEY, rgb)
fun VisualObject3D.color(rgb: Int) {
setProperty(MATERIAL_COLOR_KEY, rgb)
}
fun VisualObject.color(r: UByte, g: UByte, b: UByte) = setProperty(
COLOR_KEY,
buildMeta {
"red" put r.toInt()
"green" put g.toInt()
"blue" put b.toInt()
}
fun VisualObject3D.color(r: UByte, g: UByte, b: UByte) = setProperty(
MATERIAL_COLOR_KEY,
Colors.rgbToMeta(r, g, b)
)
var VisualObject.color: String?
get() = getProperty(COLOR_KEY).string
/**
* Web colors representation of the color in `#rrggbb` format or HTML name
*/
var VisualObject3D.color: String?
get() = getProperty(MATERIAL_COLOR_KEY)?.let { Colors.fromMeta(it) }
set(value) {
if (value != null) {
color(value)
}
setProperty(MATERIAL_COLOR_KEY, value)
}
var VisualObject.material: Material3D?
get() = getProperty(MATERIAL_KEY).node?.let { Material3D.wrap(it) }
set(value) = setProperty(MATERIAL_KEY, value?.config)
//var VisualObject3D.material: Material3D?
// get() = getProperty(MATERIAL_KEY).node?.let { Material3D.wrap(it) }
// set(value) = setProperty(MATERIAL_KEY, value?.config)
fun VisualObject.material(builder: Material3D.() -> Unit) {
material = Material3D.build(builder)
fun VisualObject3D.material(builder: Material3D.() -> Unit) {
val node = config[Material3D.MATERIAL_KEY].node
if (node != null) {
Material3D.update(node, builder)
} else {
config[Material3D.MATERIAL_KEY] = Material3D.build(builder)
}
}
var VisualObject.opacity: Double?
get() = getProperty(OPACITY_KEY).double
var VisualObject3D.opacity: Double?
get() = getProperty(MATERIAL_OPACITY_KEY).double
set(value) {
setProperty(OPACITY_KEY, value)
setProperty(MATERIAL_OPACITY_KEY, value)
}

View File

@ -5,17 +5,13 @@ package hep.dataforge.vis.spatial
import hep.dataforge.io.serialization.ConfigSerializer
import hep.dataforge.io.serialization.NameSerializer
import hep.dataforge.meta.Config
import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaItem
import hep.dataforge.meta.get
import hep.dataforge.names.Name
import hep.dataforge.names.NameToken
import hep.dataforge.names.asName
import hep.dataforge.names.plus
import hep.dataforge.vis.common.AbstractVisualObject
import hep.dataforge.vis.common.MutableVisualGroup
import hep.dataforge.vis.common.VisualGroup
import hep.dataforge.vis.common.VisualObject
import hep.dataforge.vis.common.*
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import kotlinx.serialization.UseSerializers
@ -43,12 +39,8 @@ class Proxy(val templateName: Name) : AbstractVisualObject(), VisualGroup, Visua
get() = (parent as? VisualGroup3D)?.getPrototype(templateName)
?: error("Template with name $templateName not found in $parent")
override fun getStyle(name: Name): Meta? = (parent as VisualGroup?)?.getStyle(name)
override fun addStyle(name: Name, meta: Meta, apply: Boolean) {
(parent as VisualGroup?)?.addStyle(name, meta, apply)
//do nothing
}
override val styleSheet: StyleSheet
get() = (parent as? VisualGroup)?.styleSheet ?: StyleSheet(this)
override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? {
return if (inherit) {
@ -77,16 +69,17 @@ class Proxy(val templateName: Name) : AbstractVisualObject(), VisualGroup, Visua
return NameToken(PROXY_CHILD_PROPERTY_PREFIX, childName.toString()) + propertyName
}
private fun prototypeFor(name: Name): VisualObject =
(prototype as? VisualGroup)?.get(name)
?: error("Prototype with name $name not found in ${this@Proxy}")
private fun prototypeFor(name: Name): VisualObject {
return (prototype as? VisualGroup)?.get(name)
?: error("Prototype with name $name not found in $this")
}
override var styles: List<Name>
override var styles: List<String>
get() = super.styles + prototype.styles
set(value) {
setProperty(VisualObject.STYLE_KEY, value.map { it.toString() })
styleChanged()
setProperty(VisualObject.STYLE_KEY, value)
updateStyles(value)
}
//override fun findAllStyles(): Laminate = Laminate((styles + prototype.styles).mapNotNull { findStyle(it) })
@ -94,9 +87,9 @@ class Proxy(val templateName: Name) : AbstractVisualObject(), VisualGroup, Visua
@Serializable
inner class ProxyChild(val name: Name) : AbstractVisualObject(), VisualGroup {
val prototype: VisualObject by lazy {
prototypeFor(name)
}
val prototype: VisualObject get() = prototypeFor(name)
override val styleSheet: StyleSheet get() = this@Proxy.styleSheet
override val children: Map<NameToken, VisualObject>
get() = (prototype as? VisualGroup)?.children?.mapValues { (key, _) ->
@ -105,12 +98,6 @@ class Proxy(val templateName: Name) : AbstractVisualObject(), VisualGroup, Visua
)
} ?: emptyMap()
override fun getStyle(name: Name): Meta? = this@Proxy.getStyle(name)
override fun addStyle(name: Name, meta: Meta, apply: Boolean) {
this@Proxy.addStyle(name, meta, apply)
}
override var properties: Config?
get() = propertyCache[name]
set(value) {
@ -176,8 +163,8 @@ fun VisualGroup3D.proxy(
): Proxy {
val existing = getPrototype(templateName)
if (existing == null) {
setPrototype(templateName,obj, attachToParent)
} else if(existing != obj) {
setPrototype(templateName, obj, attachToParent)
} else if (existing != obj) {
error("Can't add different prototype on top of existing one")
}
return ref(templateName, name, block)

View File

@ -12,12 +12,12 @@ import hep.dataforge.io.serialization.ConfigSerializer
import hep.dataforge.io.serialization.MetaSerializer
import hep.dataforge.io.serialization.NameSerializer
import hep.dataforge.meta.Config
import hep.dataforge.meta.Meta
import hep.dataforge.names.Name
import hep.dataforge.names.NameToken
import hep.dataforge.names.asName
import hep.dataforge.names.isEmpty
import hep.dataforge.vis.common.AbstractVisualGroup
import hep.dataforge.vis.common.StyleSheet
import hep.dataforge.vis.common.VisualGroup
import hep.dataforge.vis.common.VisualObject
import kotlinx.serialization.SerialName
@ -37,9 +37,10 @@ class VisualGroup3D : AbstractVisualGroup(), VisualObject3D {
field = value
}
override val styleSheet: StyleSheet = StyleSheet(this)
//FIXME to be lifted to AbstractVisualGroup after https://github.com/Kotlin/kotlinx.serialization/issues/378 is fixed
override var properties: Config? = null
override val styleSheet = HashMap<Name, Meta>()
override var position: Point3D? = null
override var rotation: Point3D? = null
@ -49,6 +50,11 @@ class VisualGroup3D : AbstractVisualGroup(), VisualObject3D {
private val _children = HashMap<NameToken, VisualObject>()
override val children: Map<NameToken, VisualObject> get() = _children
init {
//Do after deserialization
attachChildren()
}
override fun removeChild(token: NameToken) {
_children.remove(token)
childrenChanged(token.asName(), null)
@ -96,26 +102,18 @@ class VisualGroup3D : AbstractVisualGroup(), VisualObject3D {
}
}
override fun attachChildren() {
super.attachChildren()
prototypes?.run {
parent = this
attachChildren()
}
}
companion object {
const val PROTOTYPES_KEY = "templates"
}
}
/**
* A fix for serialization bug that writes all proper parents inside the tree after deserialization
*/
fun VisualGroup.attachChildren() {
this.children.values.forEach {
it.parent = this
(it as? VisualGroup)?.attachChildren()
}
if (this is VisualGroup3D) {
prototypes?.also {
it.parent = this
it.attachChildren()
}
}
}
fun VisualGroup3D.group(key: String = "", action: VisualGroup3D.() -> Unit = {}): VisualGroup3D =
VisualGroup3D().apply(action).also { set(key, it) }

View File

@ -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)
}

View File

@ -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
}

View File

@ -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

View File

@ -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)
}
}

View File

@ -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 {

View File

@ -35,7 +35,7 @@ abstract class MeshThreeFactory<in T : VisualObject3D>(
//JS sometimes tries to pass Geometry as BufferGeometry
@Suppress("USELESS_IS_CHECK") if (geometry !is BufferGeometry) error("BufferGeometry expected")
val meshMeta: Meta = obj.properties[Material3D.MATERIAL_KEY]?.node ?: Meta.empty
//val meshMeta: Meta = obj.properties[Material3D.MATERIAL_KEY]?.node ?: Meta.empty
val mesh = Mesh(geometry, MeshBasicMaterial()).apply {
matrixAutoUpdate = false

View File

@ -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
}
}

View File

@ -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")

View File

@ -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
}
}

View File

@ -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>()
}

View File

@ -2,13 +2,20 @@ package hep.dataforge.vis.spatial.fx
import hep.dataforge.context.*
import hep.dataforge.meta.Meta
import hep.dataforge.meta.boolean
import hep.dataforge.meta.get
import hep.dataforge.provider.Type
import hep.dataforge.vis.spatial.*
import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_KEY
import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_WIREFRAME_KEY
import hep.dataforge.vis.spatial.fx.FX3DFactory.Companion.TYPE
import javafx.scene.Group
import javafx.scene.Node
import javafx.scene.shape.CullFace
import javafx.scene.shape.DrawMode
import javafx.scene.shape.Shape3D
import javafx.scene.text.Font
import javafx.scene.text.Text
import javafx.scene.transform.Rotate
import org.fxyz3d.shapes.composites.PolyLine3D
import org.fxyz3d.shapes.primitives.CuboidMesh
@ -56,11 +63,18 @@ class FX3DPlugin : AbstractPlugin() {
} else {
FXShapeFactory(obj, binding)
}
is Label3D -> Text(obj.text).apply {
font = Font.font(obj.fontFamily, obj.fontSize)
x = -layoutBounds.width / 2
y = layoutBounds.height / 2
}
is PolyLine -> PolyLine3D(
obj.points.map { it.point },
obj.thickness.toFloat(),
obj.material?.get("color")?.color()
)
obj.getProperty(Material3D.MATERIAL_COLOR_KEY)?.color()
).apply {
this.meshView.cullFace = CullFace.FRONT
}
else -> {
//find specialized factory for this type if it is present
val factory: FX3DFactory<VisualObject3D>? = findObjectFactory(obj::class)
@ -79,15 +93,15 @@ class FX3DPlugin : AbstractPlugin() {
scaleZProperty().bind(binding[VisualObject3D.zScale].float(obj.scaleZ.toFloat()))
val rotateX = Rotate(0.0, Rotate.X_AXIS).apply {
angleProperty().bind(binding[VisualObject3D.xRotation].float(obj.rotationX.toFloat()))
angleProperty().bind(binding[VisualObject3D.xRotation].float(obj.rotationX.toFloat()).multiply(180.0 / PI))
}
val rotateY = Rotate(0.0, Rotate.Y_AXIS).apply {
angleProperty().bind(binding[VisualObject3D.yRotation].float(obj.rotationY.toFloat()))
angleProperty().bind(binding[VisualObject3D.yRotation].float(obj.rotationY.toFloat()).multiply(180.0 / PI))
}
val rotateZ = Rotate(0.0, Rotate.Z_AXIS).apply {
angleProperty().bind(binding[VisualObject3D.zRotation].float(obj.rotationZ.toFloat()))
angleProperty().bind(binding[VisualObject3D.zRotation].float(obj.rotationZ.toFloat()).multiply(180.0 / PI))
}
when (obj.rotationOrder) {
@ -100,9 +114,17 @@ class FX3DPlugin : AbstractPlugin() {
}
if (this is Shape3D) {
materialProperty().bind(binding[Material3D.MATERIAL_KEY].transform {
materialProperty().bind(binding[MATERIAL_KEY].transform {
it.material()
})
drawModeProperty().bind(binding[MATERIAL_WIREFRAME_KEY].transform {
if (it.boolean == true) {
DrawMode.LINE
} else {
DrawMode.FILL
}
})
}
}
}

View File

@ -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
}
}

View File

@ -1,25 +1,57 @@
package hep.dataforge.vis.spatial.fx
import eu.mihosoft.jcsg.CSG
import eu.mihosoft.jcsg.Polygon
import eu.mihosoft.vvecmath.Vector3d
import hep.dataforge.vis.spatial.Composite
import hep.dataforge.vis.spatial.CompositeType
import javafx.scene.Group
import javafx.scene.Node
import javafx.scene.shape.MeshView
import org.fxyz3d.utils.MeshUtils
import javafx.scene.shape.TriangleMesh
import javafx.scene.shape.VertexFormat
import java.util.*
import kotlin.collections.HashMap
import kotlin.reflect.KClass
class FXCompositeFactory(val plugin: FX3DPlugin) :
FX3DFactory<Composite> {
private fun MeshView.toCSG(): CSG {
val mesh = this.mesh as TriangleMesh
if (mesh.vertexFormat != VertexFormat.POINT_TEXCOORD) error("Not POINT_TEXCOORD")
val polygons: MutableList<Polygon> = ArrayList()
val faces = mesh.faces
val points = mesh.points
val vectorCache = HashMap<Int, Vector3d>()
fun getVector(index: Int) = vectorCache.getOrPut(index) {
Vector3d.xyz(
points[3 * index].toDouble(),
points[3 * index + 1].toDouble(),
points[3 * index + 2].toDouble()
)
}
for (i in 0 until faces.size() / 6) {
val polygon = Polygon.fromPoints(
getVector(faces[6 * i]),
getVector(faces[6 * i + 2]),
getVector(faces[6 * i + 4])
)
polygons.add(polygon)
}
return CSG.fromPolygons(polygons)
}
class FXCompositeFactory(val plugin: FX3DPlugin) : FX3DFactory<Composite> {
override val type: KClass<in Composite>
get() = Composite::class
override fun invoke(obj: Composite, binding: VisualObjectFXBinding): Node {
val first = plugin.buildNode(obj.first) as? MeshView ?: error("Can't build node")
val second = plugin.buildNode(obj.second) as? MeshView ?: error("Can't build node")
val firstCSG = MeshUtils.mesh2CSG(first)
val secondCSG = MeshUtils.mesh2CSG(second)
val resultCSG = when(obj.compositeType){
val firstCSG = first.toCSG()
val secondCSG = second.toCSG()
val resultCSG = when (obj.compositeType) {
CompositeType.UNION -> firstCSG.union(secondCSG)
CompositeType.INTERSECT -> firstCSG.intersect(secondCSG)
CompositeType.SUBTRACT -> firstCSG.difference(secondCSG)
@ -28,9 +60,9 @@ class FXCompositeFactory(val plugin: FX3DPlugin) :
}
}
internal fun CSG.toNode(): Node{
internal fun CSG.toNode(): Node {
val meshes = toJavaFXMesh().asMeshViews
return if(meshes.size == 1){
return if (meshes.size == 1) {
meshes.first()
} else {
Group(meshes.map { it })

View File

@ -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
}
}
}

View File

@ -7,6 +7,7 @@ import hep.dataforge.vis.spatial.Shape
import javafx.scene.shape.Mesh
import javafx.scene.shape.MeshView
import javafx.scene.shape.TriangleMesh
import javafx.scene.shape.VertexFormat
import org.fxyz3d.geometry.Face3
import kotlin.reflect.KClass
@ -42,13 +43,16 @@ private class FXGeometryBuilder : GeometryBuilder<Mesh> {
}
override fun build(): Mesh {
val mesh = TriangleMesh()
val mesh = TriangleMesh(VertexFormat.POINT_TEXCOORD)
vertices.forEach {
//TODO optimize copy
mesh.points.addAll(it.x.toFloat(), it.y.toFloat(), it.z.toFloat())
}
mesh.texCoords.addAll(0f, 0f)
faces.forEach {
mesh.faces.addAll(it.p0, it.p1, it.p2)
mesh.faces.addAll(it.p0, 0, it.p1, 0, it.p2, 0)
}
return mesh
}

View File

@ -2,9 +2,10 @@ package hep.dataforge.vis.spatial.fx
import hep.dataforge.meta.*
import hep.dataforge.names.Name
import hep.dataforge.names.isEmpty
import hep.dataforge.names.startsWith
import hep.dataforge.names.toName
import hep.dataforge.vis.common.VisualObject
import javafx.application.Platform
import javafx.beans.binding.ObjectBinding
import tornadofx.*
@ -16,12 +17,17 @@ class VisualObjectFXBinding(val obj: VisualObject) {
init {
obj.onPropertyChange(this) { name, _, _ ->
var currentName = name
while(!currentName.isEmpty()) {
//recursively update all upper level bindings
bindings[currentName]?.invalidate()
currentName = currentName.cutLast()
bindings.filter { it.key.startsWith(name) }.forEach { entry ->
Platform.runLater {
entry.value.invalidate()
}
}
// var currentName = name
// while (!currentName.isEmpty()) {
// //recursively update all upper level bindings
// bindings[currentName]?.invalidate()
// currentName = currentName.cutLast()
// }
}
}
@ -46,9 +52,9 @@ fun ObjectBinding<MetaItem<*>?>.long() = objectBinding { it.long }
fun ObjectBinding<MetaItem<*>?>.node() = objectBinding { it.node }
fun ObjectBinding<MetaItem<*>?>.string(default: String) = stringBinding { it.string ?: default }
fun ObjectBinding<MetaItem<*>?>.double(default: Double) = objectBinding { it.double ?: default }
fun ObjectBinding<MetaItem<*>?>.float(default: Float) = objectBinding { it.float ?: default }
fun ObjectBinding<MetaItem<*>?>.int(default: Int) = objectBinding { it.int ?: default }
fun ObjectBinding<MetaItem<*>?>.long(default: Long) = objectBinding { it.long ?:default }
fun ObjectBinding<MetaItem<*>?>.double(default: Double) = doubleBinding { it.double ?: default }
fun ObjectBinding<MetaItem<*>?>.float(default: Float) = floatBinding { it.float ?: default }
fun ObjectBinding<MetaItem<*>?>.int(default: Int) = integerBinding { it.int ?: default }
fun ObjectBinding<MetaItem<*>?>.long(default: Long) = longBinding { it.long ?: default }
fun <T> ObjectBinding<MetaItem<*>?>.transform(transform: (MetaItem<*>) -> T) = objectBinding { it?.let(transform) }

View File

@ -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()) {

View 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")
}

View File

@ -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)
}
}
}

View File

@ -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)
}

View File

@ -1,20 +1,13 @@
package hep.dataforge.vis.spatial.demo
import hep.dataforge.context.AbstractPlugin
import hep.dataforge.context.Context
import hep.dataforge.context.PluginFactory
import hep.dataforge.context.PluginTag
import hep.dataforge.context.Global
import hep.dataforge.meta.Meta
import hep.dataforge.meta.buildMeta
import hep.dataforge.meta.get
import hep.dataforge.meta.string
import hep.dataforge.names.Name
import hep.dataforge.names.toName
import hep.dataforge.output.OutputManager
import hep.dataforge.output.Renderer
import hep.dataforge.vis.common.VisualObject
import hep.dataforge.vis.spatial.VisualGroup3D
import hep.dataforge.vis.spatial.render
import hep.dataforge.vis.spatial.three.ThreeCanvas
import hep.dataforge.vis.spatial.three.ThreePlugin
import hep.dataforge.vis.spatial.three.output
@ -25,31 +18,25 @@ import kotlinx.html.hr
import kotlinx.html.id
import kotlinx.html.js.div
import kotlinx.html.span
import org.w3c.dom.Element
import kotlin.browser.document
import kotlin.dom.clear
import kotlin.reflect.KClass
class ThreeDemoGrid(meta: Meta) : AbstractPlugin(meta), OutputManager {
override val tag: PluginTag get() = Companion.tag
class ThreeDemoGrid(element: Element, meta: Meta = Meta.empty) : OutputManager {
private val gridRoot = document.create.div("row")
private val outputs: MutableMap<Name, ThreeCanvas> = HashMap()
init {
require(ThreePlugin)
}
private val three = Global.plugins.fetch(ThreePlugin)
override fun attach(context: Context) {
super.attach(context)
val elementId = meta["elementID"].string ?: "canvas"
val element = document.getElementById(elementId) ?: error("Element with id $elementId not found on page")
init {
element.clear()
element.append(gridRoot)
}
@Suppress("UNCHECKED_CAST")
override fun <T : Any> get(type: KClass<out T>, name: Name, stage: Name, meta: Meta): Renderer<T> {
val three = context.plugins.get<ThreePlugin>()!!
return outputs.getOrPut(name) {
if (type != VisualObject::class) error("Supports only DisplayObject")
@ -63,7 +50,7 @@ class ThreeDemoGrid(meta: Meta) : AbstractPlugin(meta), OutputManager {
gridRoot.append {
span("border") {
div("col-6") {
div { id = "output-$name" }.also{
div { id = "output-$name" }.also {
output.attach(it)
}
hr()
@ -75,20 +62,5 @@ class ThreeDemoGrid(meta: Meta) : AbstractPlugin(meta), OutputManager {
output
} as Renderer<T>
}
companion object : PluginFactory<ThreeDemoGrid> {
override val tag: PluginTag = PluginTag(group = "hep.dataforge", name = "vis.js.spatial.demo")
override val type: KClass<out ThreeDemoGrid> = ThreeDemoGrid::class
override fun invoke(meta: Meta,context: Context): ThreeDemoGrid = ThreeDemoGrid(meta)
}
}
fun ThreeDemoGrid.demo(name: String, title: String = name, block: VisualGroup3D.() -> Unit) {
val meta = buildMeta {
"title" put title
}
val output = get(VisualObject::class, name.toName(), meta = meta)
output.render(action = block)
}

View File

@ -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>()
}

View File

@ -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)
}

View File

@ -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
// }
// }
// }
//}

View File

@ -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)
}