diff --git a/dataforge-vis-common/build.gradle.kts b/dataforge-vis-common/build.gradle.kts index 2898a778..678a9e72 100644 --- a/dataforge-vis-common/build.gradle.kts +++ b/dataforge-vis-common/build.gradle.kts @@ -19,6 +19,8 @@ kotlin { dependencies { api("hep.dataforge:dataforge-output-html:$dataforgeVersion") api(npm("text-encoding")) + api("org.jetbrains:kotlin-extensions:1.0.1-pre.83-kotlin-1.3.50") + api(npm("core-js")) } } } 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 ca100ac8..38ddf19f 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 @@ -19,14 +19,17 @@ abstract class AbstractVisualGroup : AbstractVisualObject(), MutableVisualGroup */ abstract override val children: Map //get() = _children - //TODO replace by custom object with get/set functionality - protected abstract val styles: MutableMap + /** + * 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? = styles[name] + override fun getStyle(name: Name): Meta? = styleSheet[name] - override fun setStyle(name: Name, meta: Meta) { + override fun addStyle(name: Name, meta: Meta, apply: Boolean) { fun VisualObject.applyStyle(name: Name, meta: Meta) { - if (style.contains(name.toString())) { + if (styles.contains(name)) { //full update //TODO do a fine grained update if (this is AbstractVisualObject) { @@ -41,8 +44,10 @@ abstract class AbstractVisualGroup : AbstractVisualObject(), MutableVisualGroup } } } - styles[name] = meta - applyStyle(name, meta) + styleSheet[name] = meta + if (apply) { + applyStyle(name, meta) + } } 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 12043a27..e63382ca 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 @@ -12,9 +12,6 @@ internal data class PropertyListener( val action: (name: Name, oldItem: MetaItem<*>?, newItem: MetaItem<*>?) -> Unit ) -/** - * Abstract implementation of [VisualObject] - */ abstract class AbstractVisualObject : VisualObject { @Transient @@ -22,13 +19,10 @@ abstract class AbstractVisualObject : VisualObject { abstract override var properties: Config? - /** - * Style(s) of the object - */ - override var style: List - get() = properties?.let { it[STYLE_KEY].stringList } ?: emptyList() + override var styles: List + get() = properties?.get(STYLE_KEY).stringList.map(String::toName) set(value) { - setProperty(STYLE_KEY, value) + setProperty(STYLE_KEY, value.map { it.toString() }) styleChanged() } @@ -67,8 +61,10 @@ abstract class AbstractVisualObject : VisualObject { /** * Collect all styles for this object in a laminate */ - protected val appliedStyles: Meta - get() = styleCache ?: Laminate(style.map { it.toName() }.mapNotNull(::findStyle)).merge().also { styleCache = it } + protected val mergedStyles: Meta + get() = styleCache ?: findAllStyles().merge().also { + styleCache = it + } /** @@ -81,9 +77,9 @@ abstract class AbstractVisualObject : VisualObject { override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? { return if (inherit) { - properties?.get(name) ?: appliedStyles[name] ?: parent?.getProperty(name, inherit) + properties?.get(name) ?: mergedStyles[name] ?: parent?.getProperty(name, inherit) } else { - properties?.get(name) ?: appliedStyles[name] + properties?.get(name) ?: mergedStyles[name] } } @@ -96,7 +92,7 @@ abstract class AbstractVisualObject : VisualObject { } } -internal fun VisualObject.findStyle(styleName: Name): Meta? { +fun VisualObject.findStyle(styleName: Name): Meta? { if (this is VisualGroup) { val style = getStyle(styleName) if (style != null) return style 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/MutableVisualGroup.kt index 8aa3e8d5..761c0b20 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/MutableVisualGroup.kt @@ -44,7 +44,7 @@ interface VisualGroup : Provider, Iterable, VisualObject { /** * Add or replace style with given name */ - fun setStyle(name: Name, meta: Meta) + fun addStyle(name: Name, meta: Meta, apply: Boolean = true) operator fun get(name: Name): VisualObject? { return when { 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 93f368a4..7bc3b262 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.MetaItem -import hep.dataforge.meta.MetaRepr +import hep.dataforge.meta.* import hep.dataforge.names.Name import hep.dataforge.names.asName import hep.dataforge.names.toName @@ -56,11 +53,16 @@ interface VisualObject : MetaRepr, Configurable { */ fun removeChangeListener(owner: Any?) - var style: List + /** + * List of names of styles applied to this object + */ + var styles: List + + fun findAllStyles(): Laminate = Laminate(styles.distinct().mapNotNull(::findStyle)) companion object { const val TYPE = "visual" - val STYLE_KEY = "style".asName() + val STYLE_KEY = "@style".asName() //const val META_KEY = "@meta" //const val TAGS_KEY = "@tags" @@ -81,5 +83,5 @@ fun VisualObject.setProperty(key: String, value: Any?) = setProperty(key.toName( * Apply style to [VisualObject] by adding it to the [style] list */ fun VisualObject.applyStyle(name: String) { - style = style + name + styles = styles + name.toName() } \ No newline at end of file diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualObjectDelegate.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualObjectDelegate.kt index c51cdfe8..1dba2c7f 100644 --- a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualObjectDelegate.kt +++ b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualObjectDelegate.kt @@ -4,6 +4,7 @@ import hep.dataforge.meta.* import hep.dataforge.names.Name import hep.dataforge.names.NameToken import hep.dataforge.names.asName +import hep.dataforge.names.toName import hep.dataforge.values.Value import kotlin.jvm.JvmName import kotlin.properties.ReadOnlyProperty @@ -35,81 +36,79 @@ class VisualObjectDelegate( } class VisualObjectDelegateWrapper( + val obj: VisualObject, val key: Name?, val default: T, val inherited: Boolean, val write: Config.(name: Name, value: T) -> Unit = { name, value -> set(name, value) }, val read: (MetaItem<*>?) -> T? -) : ReadWriteProperty { +) : ReadWriteProperty { //private var cachedName: Name? = null - override fun getValue(thisRef: VisualObject, property: KProperty<*>): T { + override fun getValue(thisRef: Any?, property: KProperty<*>): T { val name = key ?: property.name.asName() - return if (inherited) { - read(thisRef.getProperty(name)) - } else { - read(thisRef.config[name]) - } ?: default + return read(obj.getProperty(name,inherited))?:default } - override fun setValue(thisRef: VisualObject, property: KProperty<*>, value: T) { + override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { val name = key ?: property.name.asName() - thisRef.config[name] = value + obj.config[name] = value } } fun VisualObject.value(default: Value? = null, key: String? = null, inherited: Boolean = false) = - VisualObjectDelegateWrapper(key?.asName(), default, inherited) { it.value } + VisualObjectDelegateWrapper(this, key?.toName(), default, inherited) { it.value } fun VisualObject.string(default: String? = null, key: String? = null, inherited: Boolean = false) = - VisualObjectDelegateWrapper(key?.asName(), default, inherited) { it.string } + VisualObjectDelegateWrapper(this, key?.toName(), default, inherited) { it.string } fun VisualObject.boolean(default: Boolean? = null, key: String? = null, inherited: Boolean = false) = - VisualObjectDelegateWrapper(key?.asName(), default, inherited) { it.boolean } + VisualObjectDelegateWrapper(this, key?.toName(), default, inherited) { it.boolean } fun VisualObject.number(default: Number? = null, key: String? = null, inherited: Boolean = false) = - VisualObjectDelegateWrapper(key?.asName(), default, inherited) { it.number } + VisualObjectDelegateWrapper(this, key?.toName(), default, inherited) { it.number } fun VisualObject.double(default: Double? = null, key: String? = null, inherited: Boolean = false) = - VisualObjectDelegateWrapper(key?.asName(), default, inherited) { it.double } + VisualObjectDelegateWrapper(this, key?.toName(), default, inherited) { it.double } fun VisualObject.int(default: Int? = null, key: String? = null, inherited: Boolean = false) = - VisualObjectDelegateWrapper(key?.asName(), default, inherited) { it.int } + VisualObjectDelegateWrapper(this, key?.toName(), default, inherited) { it.int } fun VisualObject.node(key: String? = null, inherited: Boolean = true) = - VisualObjectDelegateWrapper(key?.asName(), null, inherited) { it.node } + VisualObjectDelegateWrapper(this, key?.toName(), null, inherited) { it.node } fun VisualObject.item(key: String? = null, inherited: Boolean = true) = - VisualObjectDelegateWrapper(key?.asName(), null, inherited) { it } + VisualObjectDelegateWrapper(this, key?.toName(), null, inherited) { it } //fun Configurable.spec(spec: Specification, key: String? = null) = ChildConfigDelegate(key) { spec.wrap(this) } @JvmName("safeString") fun VisualObject.string(default: String, key: String? = null, inherited: Boolean = false) = - VisualObjectDelegateWrapper(key?.asName(), default, inherited) { it.string } + VisualObjectDelegateWrapper(this, key?.toName(), default, inherited) { it.string } @JvmName("safeBoolean") fun VisualObject.boolean(default: Boolean, key: String? = null, inherited: Boolean = false) = - VisualObjectDelegateWrapper(key?.asName(), default, inherited) { it.boolean } + VisualObjectDelegateWrapper(this, key?.toName(), default, inherited) { it.boolean } @JvmName("safeNumber") fun VisualObject.number(default: Number, key: String? = null, inherited: Boolean = false) = - VisualObjectDelegateWrapper(key?.asName(), default, inherited) { it.number } + VisualObjectDelegateWrapper(this, key?.toName(), default, inherited) { it.number } @JvmName("safeDouble") fun VisualObject.double(default: Double, key: String? = null, inherited: Boolean = false) = - VisualObjectDelegateWrapper(key?.asName(), default, inherited) { it.double } + VisualObjectDelegateWrapper(this, key?.toName(), default, inherited) { it.double } @JvmName("safeInt") fun VisualObject.int(default: Int, key: String? = null, inherited: Boolean = false) = - VisualObjectDelegateWrapper(key?.asName(), default, inherited) { it.int } + VisualObjectDelegateWrapper(this, key?.toName(), default, inherited) { it.int } inline fun > VisualObject.enum(default: E, key: String? = null, inherited: Boolean = false) = VisualObjectDelegateWrapper( + this, key?.let { NameToken(it).asName() }, default, inherited @@ -121,11 +120,11 @@ fun VisualObject.merge( key: String? = null, transformer: (Sequence>) -> T ): ReadOnlyProperty { - return object : ReadOnlyProperty { - override fun getValue(thisRef: VisualObject, property: KProperty<*>): T { - val name = key?.asName() ?: property.name.asName() + return object : ReadOnlyProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): T { + val name = key?.toName() ?: property.name.asName() val sequence = sequence> { - var thisObj: VisualObject? = thisRef + var thisObj: VisualObject? = this@merge while (thisObj != null) { thisObj.config[name]?.let { yield(it) } thisObj = thisObj.parent diff --git a/dataforge-vis-common/src/jsMain/kotlin/hep.dataforge.vis.hmr/HMR.kt b/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/HMR.kt similarity index 97% rename from dataforge-vis-common/src/jsMain/kotlin/hep.dataforge.vis.hmr/HMR.kt rename to dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/HMR.kt index 37b4c4c1..5353fb03 100644 --- a/dataforge-vis-common/src/jsMain/kotlin/hep.dataforge.vis.hmr/HMR.kt +++ b/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/HMR.kt @@ -1,4 +1,4 @@ -package hep.dataforge.vis.hmr +package hep.dataforge.vis import kotlin.browser.document import kotlin.dom.hasClass diff --git a/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/jsExtra.kt b/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/jsExtra.kt new file mode 100644 index 00000000..56751272 --- /dev/null +++ b/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/jsExtra.kt @@ -0,0 +1,21 @@ +package hep.dataforge.vis + +inline fun jsObject(builder: T.() -> Unit): T { + val obj: T = js("({})") as T + return obj.apply { + builder() + } +} + +inline fun js(builder: dynamic.() -> Unit): dynamic = jsObject(builder) + +//fun clone(obj: T) = objectAssign(jsObject {}, obj) + +//inline fun assign(obj: T, builder: T.() -> Unit) = clone(obj).apply(builder) + +fun toPlainObjectStripNull(obj: Any) = js { + for (key in Object.keys(obj)) { + val value = obj.asDynamic()[key] + if (value != null) this[key] = value + } +} \ 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 381e32f1..ee646f7a 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 @@ -14,7 +14,6 @@ import hep.dataforge.vis.spatial.VisualGroup3D import hep.dataforge.vis.spatial.VisualObject3D import hep.dataforge.vis.spatial.rotationOrder import scientifik.gdml.* -import kotlin.collections.set import kotlin.random.Random class GDMLTransformer(val root: GDML) { @@ -31,7 +30,7 @@ class GDMLTransformer(val root: GDML) { * A special group for local templates */ val templates by lazy { VisualGroup3D() } - private val styles = HashMap() + private val styleCache = HashMap() var lUnit: LUnit = LUnit.MM @@ -42,7 +41,7 @@ class GDMLTransformer(val root: GDML) { var solidConfiguration: VisualObject3D.(parent: GDMLVolume, solid: GDMLSolid) -> Unit = { _, _ -> } fun VisualObject.useStyle(name: String, builder: MetaBuilder.() -> Unit) { - styles.getOrPut(name.toName()){ + styleCache.getOrPut(name.toName()){ buildMeta(builder) } applyStyle(name) @@ -60,35 +59,17 @@ class GDMLTransformer(val root: GDML) { obj.solidConfiguration(parent, solid) } - - fun printStatistics() { - println("Solids:") - solidCounter.entries.sortedByDescending { it.value }.forEach { - println("\t$it") - } - println("Solids total: ${solidCounter.values.sum()}") - } - - private val solidCounter = HashMap() - - internal fun solidAdded(solid: GDMLSolid) { - solidCounter[solid.name] = (solidCounter[solid.name] ?: 0) + 1 - } +// +// internal fun solidAdded(solid: GDMLSolid) { +// solidCounter[solid.name] = (solidCounter[solid.name] ?: 0) + 1 +// } var onFinish: GDMLTransformer.() -> Unit = {} - var optimizeSingleChild = false - - //var optimizations: List = emptyList() - internal fun finalize(final: VisualGroup3D): VisualGroup3D { -// var res = final -// optimizations.forEach { -// res = it(res) -// } final.templates = templates - styles.forEach { - final.setStyle(it.key, it.value) + styleCache.forEach { + final.addStyle(it.key, it.value, false) } final.rotationOrder = RotationOrder.ZXY onFinish(this@GDMLTransformer) diff --git a/dataforge-vis-spatial-gdml/src/commonMain/kotlin/hep/dataforge/vis/spatial/gdml/optimizations.kt b/dataforge-vis-spatial-gdml/src/commonMain/kotlin/hep/dataforge/vis/spatial/gdml/optimizations.kt deleted file mode 100644 index bf077d72..00000000 --- a/dataforge-vis-spatial-gdml/src/commonMain/kotlin/hep/dataforge/vis/spatial/gdml/optimizations.kt +++ /dev/null @@ -1,58 +0,0 @@ -package hep.dataforge.vis.spatial.gdml - -import hep.dataforge.meta.update -import hep.dataforge.names.asName -import hep.dataforge.vis.common.MutableVisualGroup -import hep.dataforge.vis.spatial.Point3D -import hep.dataforge.vis.spatial.VisualGroup3D -import hep.dataforge.vis.spatial.VisualObject3D -import hep.dataforge.vis.spatial.plus -import kotlin.collections.component1 -import kotlin.collections.component2 - -typealias GDMLOptimization = GDMLTransformer.(VisualGroup3D) -> VisualGroup3D - -/** - * Collapse nodes with single child - */ -val optimizeSingleChild: GDMLOptimization = { tree -> - fun MutableVisualGroup.replaceChildren() { - children.forEach { (key, child) -> - if (child is VisualGroup3D && child.children.size == 1) { - val newChild = child.children.values.first().apply { - config.update(child.config) - } - - if (newChild is VisualObject3D) { - newChild.apply { - position += child.position - rotation += child.rotation - scale = when { - scale == null && child.scale == null -> null - scale == null -> child.scale - child.scale == null -> scale - else -> Point3D( - scale!!.x * child.scale!!.x, - scale!!.y * child.scale!!.y, - scale!!.z * child.scale!!.z - ) - } - } - } - - if (newChild is MutableVisualGroup) { - newChild.replaceChildren() - } - - //actual replacement - set(key.asName(), newChild) - } else if (child is MutableVisualGroup) { - child.replaceChildren() - } - } - } - - tree.replaceChildren() - - tree -} \ No newline at end of file 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 e3935a0f..3271fe38 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 @@ -47,7 +47,7 @@ private fun VisualGroup3D.addSolid( name: String = "", block: VisualObject3D.() -> Unit = {} ): VisualObject3D { - context.solidAdded(solid) + //context.solidAdded(solid) val lScale = solid.lscale(context.lUnit) val aScale = solid.ascale() return when (solid) { @@ -150,31 +150,13 @@ private fun VisualGroup3D.addPhysicalVolume( when (context.volumeAction(volume)) { GDMLTransformer.Action.ACCEPT -> { val group = volume(context, volume) - //optimizing single child case - if (context.optimizeSingleChild && group.children.size == 1) { - this[physVolume.name ?: ""] = group.children.values.first().apply { - //Must set this to avoid parent reset error - parent = null - //setting offset from physical volume - withPosition( - context.lUnit, - physVolume.resolvePosition(context.root), - physVolume.resolveRotation(context.root), - physVolume.resolveScale(context.root) - ) - // in case when both phys volume and underlying volume have offset - position += group.position - rotation += group.rotation - } - } else { - this[physVolume.name ?: ""] = group.apply { - withPosition( - context.lUnit, - physVolume.resolvePosition(context.root), - physVolume.resolveRotation(context.root), - physVolume.resolveScale(context.root) - ) - } + this[physVolume.name ?: ""] = group.apply { + withPosition( + context.lUnit, + physVolume.resolvePosition(context.root), + physVolume.resolveRotation(context.root), + physVolume.resolveScale(context.root) + ) } } GDMLTransformer.Action.CACHE -> { 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 63a255c0..cacc796b 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 @@ -1,33 +1,26 @@ package hep.dataforge.vis.spatial.gdml.demo import hep.dataforge.context.Global -import hep.dataforge.vis.common.VisualGroup -import hep.dataforge.vis.hmr.ApplicationBase -import hep.dataforge.vis.hmr.startApplication +import hep.dataforge.vis.ApplicationBase import hep.dataforge.vis.spatial.Material3D.Companion.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.editor.visualObjectTree import hep.dataforge.vis.spatial.gdml.GDMLTransformer import hep.dataforge.vis.spatial.gdml.LUnit import hep.dataforge.vis.spatial.gdml.toVisual -import hep.dataforge.vis.spatial.three.ThreeOutput import hep.dataforge.vis.spatial.three.ThreePlugin import hep.dataforge.vis.spatial.three.output -import hep.dataforge.vis.spatial.tree.propertyEditor -import hep.dataforge.vis.spatial.tree.render -import hep.dataforge.vis.spatial.tree.toTree -import kotlinx.html.InputType +import hep.dataforge.vis.startApplication import kotlinx.html.dom.append -import kotlinx.html.js.input -import kotlinx.html.js.li import kotlinx.html.js.p -import kotlinx.html.js.ul -import org.w3c.dom.Element +import org.w3c.dom.DragEvent import org.w3c.dom.HTMLDivElement import org.w3c.dom.HTMLElement -import org.w3c.dom.events.Event import org.w3c.files.FileList import org.w3c.files.FileReader import org.w3c.files.get @@ -40,33 +33,27 @@ private class GDMLDemoApp : ApplicationBase() { /** * Handle mouse drag according to https://www.html5rocks.com/en/tutorials/file/dndfiles/ */ - private fun handleDragOver(event: Event) { + private fun handleDragOver(event: DragEvent) { event.stopPropagation() event.preventDefault() - event.asDynamic().dataTransfer.dropEffect = "copy" + event.dataTransfer?.dropEffect = "copy" } /** * Load data from text file */ - private fun loadData(event: Event, block: (name: String, data: String) -> Unit) { + private fun loadData(event: DragEvent, block: (name: String, data: String) -> Unit) { event.stopPropagation() event.preventDefault() - val file = (event.asDynamic().dataTransfer.files as FileList)[0] + val file = (event.dataTransfer?.files as FileList)[0] ?: throw RuntimeException("Failed to load file") FileReader().apply { onload = { val string = result as String - -// try { - block(file.name, string) -// } catch (ex: Exception) { -// console.error(ex) -// } - + block(file.name, string) } readAsText(file) } @@ -99,53 +86,24 @@ private class GDMLDemoApp : ApplicationBase() { } } - fun setupLayers(element: Element, output: ThreeOutput) { - element.clear() - element.append { - ul("list-group") { - (0..9).forEach { layer -> - li("list-group-item") { - +"layer $layer" - input(type = InputType.checkBox).apply { - if (layer == 0) { - checked = true - } - onchange = { - if (checked) { - output.camera.layers.enable(layer) - } else { - output.camera.layers.disable(layer) - } - } - } - } - } - } - } - } - private val gdmlConfiguration: GDMLTransformer.() -> Unit = { lUnit = LUnit.CM volumeAction = { volume -> when { volume.name.startsWith("ecal01lay") -> GDMLTransformer.Action.REJECT - volume.name.startsWith("ecal") -> GDMLTransformer.Action.CACHE volume.name.startsWith("UPBL") -> GDMLTransformer.Action.REJECT volume.name.startsWith("USCL") -> GDMLTransformer.Action.REJECT - volume.name.startsWith("U") -> GDMLTransformer.Action.CACHE volume.name.startsWith("VPBL") -> GDMLTransformer.Action.REJECT volume.name.startsWith("VSCL") -> GDMLTransformer.Action.REJECT - volume.name.startsWith("V") -> GDMLTransformer.Action.CACHE - else -> GDMLTransformer.Action.ACCEPT + else -> GDMLTransformer.Action.CACHE } } solidConfiguration = { parent, solid -> - if (parent.physVolumes.isNotEmpty() - || solid.name.startsWith("Coil") - || solid.name.startsWith("Yoke") - || solid.name.startsWith("Magnet") + if ( + solid.name.startsWith("Yoke") || solid.name.startsWith("Pole") + || parent.physVolumes.isNotEmpty() ) { useStyle("opaque") { OPACITY_KEY to 0.3 @@ -185,18 +143,17 @@ private class GDMLDemoApp : ApplicationBase() { } } + //Optimize tree + //(visual as? VisualGroup3D)?.transformInPlace(UnRef, RemoveSingleChild) + message("Rendering") - val output = three.output(canvas as HTMLElement) //output.camera.layers.enable(1) - output.camera.layers.set(0) - setupLayers(layers, output) + val output = three.output(canvas as HTMLElement) - if (visual is VisualGroup) { - visual.toTree(editor::propertyEditor).render(tree as HTMLElement) { - showCheckboxes = false - } - } + output.camera.layers.set(0) + layers.threeOutputConfig(output) + tree.visualObjectTree(visual, editor::propertyEditor) output.render(visual) message(null) @@ -204,8 +161,8 @@ private class GDMLDemoApp : ApplicationBase() { } (document.getElementById("drop_zone") as? HTMLDivElement)?.apply { - addEventListener("dragover", { handleDragOver(it) }, false) - addEventListener("drop", { loadData(it, action) }, false) + addEventListener("dragover", { handleDragOver(it as DragEvent) }, false) + addEventListener("drop", { loadData(it as DragEvent, action) }, false) } } diff --git a/dataforge-vis-spatial-gdml/src/jsMain/web/index.html b/dataforge-vis-spatial-gdml/src/jsMain/web/index.html index f99f2803..22603bcf 100644 --- a/dataforge-vis-spatial-gdml/src/jsMain/web/index.html +++ b/dataforge-vis-spatial-gdml/src/jsMain/web/index.html @@ -29,45 +29,12 @@
-
-
- -
-
-
-
-

- -

-
- -
-
-
-
-
-
-
-
-

- -

-
-
-
-
-
-
-
-
+
+
+
+
+
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 new file mode 100644 index 00000000..b0ff5b38 --- /dev/null +++ b/dataforge-vis-spatial-gdml/src/jvmMain/kotlin/hep/dataforge/vis/spatial/gdml/testCubes.kt @@ -0,0 +1,40 @@ +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/testMain.kt b/dataforge-vis-spatial-gdml/src/jvmMain/kotlin/hep/dataforge/vis/spatial/gdml/testMain.kt index 019a8856..7ef8a918 100644 --- 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 @@ -1,21 +1,30 @@ 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 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 + 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") @@ -24,16 +33,14 @@ fun main() { || solid.name.startsWith("Pole") ) { useStyle("opaque") { - opacity = 0.3 + Material3D.OPACITY_KEY to 0.3 } } } - -// optimizeSingleChild = true - //optimizations = listOf(optimizeSingleChild) - onFinish = { printStatistics() } } + // (visual as? VisualGroup3D)?.let { UnRef(it) }?.let { RemoveSingleChild(it) } + val string = Visual3DPlugin.json.stringify(VisualGroup3D.serializer(), visual) val tmpFile = File.createTempFile("dataforge-visual", ".json") diff --git a/dataforge-vis-spatial/build.gradle.kts b/dataforge-vis-spatial/build.gradle.kts index ab4f5ce7..41672aee 100644 --- a/dataforge-vis-spatial/build.gradle.kts +++ b/dataforge-vis-spatial/build.gradle.kts @@ -27,14 +27,9 @@ kotlin { } jsMain { dependencies { + api(project(":wrappers")) implementation(npm("three", "0.106.2")) implementation(npm("@hi-level/three-csg", "1.0.6")) - implementation(npm("style-loader")) - implementation(npm("inspire-tree","6.0.1")) - implementation(npm("inspire-tree-dom","4.0.6")) - implementation(npm("jsoneditor")) -// api("org.jetbrains:kotlin-extensions:1.0.1-pre.83-kotlin-1.3.50") -// api(npm("core-js")) } } } diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/PolyLine.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/PolyLine.kt new file mode 100644 index 00000000..eb8b4699 --- /dev/null +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/PolyLine.kt @@ -0,0 +1,26 @@ +@file:UseSerializers(Point3DSerializer::class) + +package hep.dataforge.vis.spatial + +import hep.dataforge.io.ConfigSerializer +import hep.dataforge.meta.Config +import hep.dataforge.vis.common.AbstractVisualObject +import hep.dataforge.vis.common.number +import kotlinx.serialization.Serializable +import kotlinx.serialization.UseSerializers + +@Serializable +class PolyLine(var points: List) : AbstractVisualObject(), VisualObject3D { + @Serializable(ConfigSerializer::class) + override var properties: Config? = null + + override var position: Point3D? = null + override var rotation: Point3D? = null + override var scale: Point3D? = null + + //var lineType by string() + var thickness by number(1.0, key = "material.thickness") +} + +fun VisualGroup3D.polyline(vararg points: Point3D, name: String = "", action: PolyLine.() -> Unit = {}) = + PolyLine(points.toList()).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/Proxy.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Proxy.kt index c01ddd1c..e4125590 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 @@ -35,21 +35,27 @@ class Proxy(val templateName: Name) : AbstractVisualObject(), VisualGroup, Visua /** * Recursively search for defined template in the parent */ - val prototype: VisualObject3D get() = (parent as? VisualGroup3D)?.getTemplate(templateName) - ?: error("Template with name $templateName not found in $parent") + val prototype: VisualObject3D + get() = (parent as? VisualGroup3D)?.getTemplate(templateName) + ?: error("Template with name $templateName not found in $parent") override fun getStyle(name: Name): Meta? = (parent as VisualGroup?)?.getStyle(name) - override fun setStyle(name: Name, meta: Meta) { - (parent as VisualGroup?)?.setStyle(name, meta) + override fun addStyle(name: Name, meta: Meta, apply: Boolean) { + (parent as VisualGroup?)?.addStyle(name, meta, apply) //do nothing } override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? { return if (inherit) { - super.getProperty(name, false) ?: prototype.getProperty(name, false) ?: parent?.getProperty(name, inherit) + properties?.get(name) + ?: mergedStyles[name] + ?: prototype.getProperty(name, false) + ?: parent?.getProperty(name, inherit) } else { - super.getProperty(name, false) ?: prototype.getProperty(name, false) + properties?.get(name) + ?: mergedStyles[name] + ?: prototype.getProperty(name, false) } } @@ -74,6 +80,15 @@ class Proxy(val templateName: Name) : AbstractVisualObject(), VisualGroup, Visua ?: error("Prototype with name $name not found in ${this@Proxy}") + override var styles: List + get() = super.styles + prototype.styles + set(value) { + setProperty(VisualObject.STYLE_KEY, value.map { it.toString() }) + styleChanged() + } + + //override fun findAllStyles(): Laminate = Laminate((styles + prototype.styles).mapNotNull { findStyle(it) }) + inner class ProxyChild(val name: Name) : AbstractVisualObject(), VisualGroup { val prototype: VisualObject by lazy { @@ -89,8 +104,8 @@ class Proxy(val templateName: Name) : AbstractVisualObject(), VisualGroup, Visua override fun getStyle(name: Name): Meta? = this@Proxy.getStyle(name) - override fun setStyle(name: Name, meta: Meta) { - this@Proxy.setStyle(name, meta) + override fun addStyle(name: Name, meta: Meta, apply: Boolean) { + this@Proxy.addStyle(name, meta, apply) } override var properties: Config? @@ -113,12 +128,12 @@ class Proxy(val templateName: Name) : AbstractVisualObject(), VisualGroup, Visua override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? { return if (inherit) { properties?.get(name) - ?: appliedStyles[name] - ?: parent?.getProperty(name, inherit) + ?: mergedStyles[name] ?: prototype.getProperty(name, inherit) + ?: parent?.getProperty(name, inherit) } else { properties?.get(name) - ?: appliedStyles[name] + ?: mergedStyles[name] ?: prototype.getProperty(name, inherit) } } @@ -141,4 +156,4 @@ inline fun VisualGroup3D.ref( templateName: Name, name: String = "", action: Proxy.() -> Unit = {} -) = Proxy(templateName).apply(action).also { set(name, it) } \ No newline at end of file +) = Proxy(templateName).apply(action).also { set(name, it) } 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 7cea1f28..410b9365 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 @@ -43,7 +43,7 @@ class VisualGroup3D : AbstractVisualGroup(), VisualObject3D { //FIXME to be lifted to AbstractVisualGroup after https://github.com/Kotlin/kotlinx.serialization/issues/378 is fixed override var properties: Config? = null - override val styles = HashMap() + override val styleSheet = HashMap() override var position: Point3D? = null override var rotation: Point3D? = null 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 0410677e..fb34174e 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 @@ -10,6 +10,7 @@ import hep.dataforge.output.Output import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.spatial.VisualObject3D.Companion.DETAIL_KEY import hep.dataforge.vis.spatial.VisualObject3D.Companion.LAYER_KEY +import hep.dataforge.vis.spatial.VisualObject3D.Companion.IGNORE_KEY import hep.dataforge.vis.spatial.VisualObject3D.Companion.SELECTED_KEY import hep.dataforge.vis.spatial.VisualObject3D.Companion.VISIBLE_KEY import kotlinx.serialization.UseSerializers @@ -40,6 +41,7 @@ interface VisualObject3D : VisualObject { val SELECTED_KEY = "selected".asName() val DETAIL_KEY = "detail".asName() val LAYER_KEY = "layer".asName() + val IGNORE_KEY = "ignore".asName() val GEOMETRY_KEY = "geometey".asName() @@ -111,6 +113,14 @@ var VisualObject.visible: Boolean? get() = getProperty(VISIBLE_KEY).boolean set(value) = setProperty(VISIBLE_KEY, value) +/** + * If this property is true, the object will be ignored on render. + * Property is not inherited. + */ +var VisualObject.ignore: Boolean? + get() = getProperty(IGNORE_KEY,false).boolean + set(value) = setProperty(IGNORE_KEY, value) + var VisualObject.selected: Boolean? get() = getProperty(SELECTED_KEY).boolean set(value) = setProperty(SELECTED_KEY, value) 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 new file mode 100644 index 00000000..e8135dc3 --- /dev/null +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/transform/RemoveSingleChild.kt @@ -0,0 +1,58 @@ +package hep.dataforge.vis.spatial.transform + +import hep.dataforge.meta.update +import hep.dataforge.names.asName +import hep.dataforge.vis.common.MutableVisualGroup +import hep.dataforge.vis.common.VisualGroup +import hep.dataforge.vis.common.VisualObject +import hep.dataforge.vis.spatial.* + +internal fun mergeChild(parent: VisualGroup, child: VisualObject): VisualObject { + return child.apply { + + parent.properties?.let { config.update(it) } + + if (this is VisualObject3D && parent is VisualObject3D) { + position += parent.position + rotation += parent.rotation + scale = when { + scale == null && parent.scale == null -> null + scale == null -> parent.scale + parent.scale == null -> scale + else -> Point3D( + scale!!.x * parent.scale!!.x, + scale!!.y * parent.scale!!.y, + scale!!.z * parent.scale!!.z + ) + } + } + + } +} + +object RemoveSingleChild : VisualTreeTransform() { + + override fun VisualGroup3D.transformInPlace() { + fun MutableVisualGroup.replaceChildren() { + children.forEach { (childName, parent) -> + if (parent is Proxy) return@forEach //ignore refs + if (parent is MutableVisualGroup) { + parent.replaceChildren() + } + if (parent is VisualGroup && parent.children.size == 1) { + val child = parent.children.values.first() + val newParent = mergeChild(parent, child) + newParent.parent = null + set(childName.asName(), newParent) + } + } + } + + replaceChildren() + templates?.replaceChildren() + } + + override fun VisualGroup3D.clone(): VisualGroup3D { + TODO() + } +} \ No newline at end of file diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/transform/UnRef.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/transform/UnRef.kt new file mode 100644 index 00000000..b09561e4 --- /dev/null +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/transform/UnRef.kt @@ -0,0 +1,49 @@ +package hep.dataforge.vis.spatial.transform + +import hep.dataforge.names.Name +import hep.dataforge.names.asName +import hep.dataforge.vis.common.MutableVisualGroup +import hep.dataforge.vis.common.VisualGroup +import hep.dataforge.vis.spatial.Proxy +import hep.dataforge.vis.spatial.VisualGroup3D + +object UnRef : VisualTreeTransform() { + private fun VisualGroup.countRefs(): Map { + return children.values.fold(HashMap()) { reducer, obj -> + if (obj is VisualGroup) { + val counter = obj.countRefs() + counter.forEach { (key, value) -> + reducer[key] = (reducer[key] ?: 0) + value + } + } else if (obj is Proxy) { + reducer[obj.templateName] = (reducer[obj.templateName] ?: 0) + 1 + } + + return reducer + } + } + + private fun MutableVisualGroup.unref(name: Name) { + (this as? VisualGroup3D)?.templates?.set(name, null) + children.filter { (it.value as? Proxy)?.templateName == name }.forEach { (key, value) -> + val proxy = value as Proxy + val newChild = mergeChild(proxy, proxy.prototype) + newChild.parent = null + set(key.asName(), newChild) // replace proxy with merged object + } + + children.values.filterIsInstance().forEach { it.unref(name) } + } + + override fun VisualGroup3D.transformInPlace() { + val counts = countRefs() + counts.filter { it.value <= 1 }.forEach { + this.unref(it.key) + } + } + + override fun VisualGroup3D.clone(): VisualGroup3D { + TODO() + } + +} \ No newline at end of file diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/transform/VisualTreeTransform.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/transform/VisualTreeTransform.kt new file mode 100644 index 00000000..596aad56 --- /dev/null +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/transform/VisualTreeTransform.kt @@ -0,0 +1,34 @@ +package hep.dataforge.vis.spatial.transform + +import hep.dataforge.vis.common.VisualObject + +/** + * A root class for [VisualObject] tree optimization + */ +abstract class VisualTreeTransform { + protected abstract fun T.transformInPlace() + protected abstract fun T.clone(): T + + operator fun invoke(source: T, inPlace: Boolean = true): T { + val newSource = if (inPlace) { + source + } else { + source.clone() + } + newSource.transformInPlace() + + return newSource + } +} + +fun T.transform(vararg transform: VisualTreeTransform): T { + var res = this + transform.forEach { + res = it(res) + } + return res +} + +fun T.transformInPlace(vararg transform: VisualTreeTransform) { + transform.forEach { it(this) } +} \ No newline at end of file diff --git a/dataforge-vis-spatial/src/commonTest/kotlin/hep/dataforge/vis/spatial/SerializationTest.kt b/dataforge-vis-spatial/src/commonTest/kotlin/hep/dataforge/vis/spatial/SerializationTest.kt index 805fee43..09d59f29 100644 --- a/dataforge-vis-spatial/src/commonTest/kotlin/hep/dataforge/vis/spatial/SerializationTest.kt +++ b/dataforge-vis-spatial/src/commonTest/kotlin/hep/dataforge/vis/spatial/SerializationTest.kt @@ -1,6 +1,6 @@ package hep.dataforge.vis.spatial -import hep.dataforge.context.Global +import hep.dataforge.vis.spatial.Visual3DPlugin.Companion.json import kotlinx.serialization.ImplicitReflectionSerializer import kotlin.test.Test import kotlin.test.assertEquals @@ -12,9 +12,9 @@ class SerializationTest { val cube = Box(100f,100f,100f).apply{ color(222) } - val meta = cube.toMeta() - println(meta) - val newCube = Box(Global,null, meta) + val string = json.stringify(Box.serializer(),cube) + println(string) + val newCube = json.parse(Box.serializer(),string) assertEquals(cube.toMeta(),newCube.toMeta()) } } \ No newline at end of file diff --git a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/editor/bootstrap.kt b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/editor/bootstrap.kt new file mode 100644 index 00000000..7be121b1 --- /dev/null +++ b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/editor/bootstrap.kt @@ -0,0 +1,15 @@ +package hep.dataforge.vis.spatial.editor + +import kotlinx.html.TagConsumer +import kotlinx.html.js.div +import kotlinx.html.js.h3 +import org.w3c.dom.HTMLElement + +inline fun TagConsumer.card(title: String, crossinline block: TagConsumer.() -> Unit) { + div("card w-100") { + div("card-body") { + h3(classes = "card-title") { +title } + block() + } + } +} \ No newline at end of file diff --git a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/tree/jsVisualTree.kt b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/editor/jsVisualTree.kt similarity index 67% rename from dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/tree/jsVisualTree.kt rename to dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/editor/jsVisualTree.kt index 84bd067d..13868fdf 100644 --- a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/tree/jsVisualTree.kt +++ b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/editor/jsVisualTree.kt @@ -1,6 +1,6 @@ @file:Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE") -package hep.dataforge.vis.spatial.tree +package hep.dataforge.vis.spatial.editor import hep.dataforge.meta.string import hep.dataforge.names.EmptyName @@ -9,21 +9,19 @@ import hep.dataforge.names.NameToken import hep.dataforge.vis.common.VisualGroup import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.common.getProperty +import hep.dataforge.vis.jsObject import hep.dataforge.vis.spatial.Proxy import hep.dataforge.vis.spatial.visible -import org.w3c.dom.HTMLElement +import info.laht.threekt.loaders.Cache.clear +import kotlinx.html.div +import kotlinx.html.dom.append +import org.w3c.dom.Element import kotlin.js.json operator fun Name.plus(other: NameToken): Name = Name(tokens + other) -fun InspireTree.render(element: HTMLElement, block: DomConfig.() -> Unit = {}) { - val config = (json( - "target" to element - ) as DomConfig).apply(block) - InspireTreeDOM(this, config) -} -internal fun createInspireTree(block: Config.() -> Unit = {}): InspireTree { +private fun createInspireTree(block: Config.() -> Unit = {}): InspireTree { val config = (json( "checkbox" to json( "autoCheckChildren" to false @@ -32,7 +30,7 @@ internal fun createInspireTree(block: Config.() -> Unit = {}): InspireTree { return InspireTree(config) } -fun VisualGroup.toTree(onFocus: (VisualObject?, String?) -> Unit = { _, _ -> }): InspireTree { +private fun VisualObject.toTree(onFocus: (VisualObject?, String?) -> Unit = { _, _ -> }): InspireTree { val map = HashMap() @@ -67,14 +65,18 @@ fun VisualGroup.toTree(onFocus: (VisualObject?, String?) -> Unit = { _, _ -> }): } - fun TreeNode.fillChildren(group: VisualGroup, groupName: Name) { - group.children.forEach { (token, obj) -> - val name = groupName + token - val nodeConfig = generateNodeConfig(obj, name) - val childNode = addChild(nodeConfig) - map[childNode.id] = obj - if (obj is VisualGroup) { - childNode.fillChildren(obj, name) + fun TreeNode.fillChildren(group: VisualObject, groupName: Name) { + if(group is VisualGroup) { + group.children.forEach { (token, obj) -> + if (!token.body.startsWith("@")) { + val name = groupName + token + val nodeConfig = generateNodeConfig(obj, name) + val childNode = addChild(nodeConfig) + map[childNode.id] = obj + if (obj is VisualGroup) { + childNode.fillChildren(obj, name) + } + } } } } @@ -119,3 +121,16 @@ fun VisualGroup.toTree(onFocus: (VisualObject?, String?) -> Unit = { _, _ -> }): return inspireTree } + +fun Element.visualObjectTree(group: VisualObject, onFocus: (VisualObject?, String?) -> Unit) { + clear() + append { + card("Visual object tree") { + val domConfig = jsObject { + target = div() + showCheckboxes = false + } + InspireTreeDOM(group.toTree(onFocus), domConfig) + } + } +} \ No newline at end of file diff --git a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/editor/outputConfig.kt b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/editor/outputConfig.kt new file mode 100644 index 00000000..f667fc99 --- /dev/null +++ b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/editor/outputConfig.kt @@ -0,0 +1,37 @@ +package hep.dataforge.vis.spatial.editor + +import hep.dataforge.vis.spatial.three.ThreeOutput +import kotlinx.html.InputType +import kotlinx.html.dom.append +import kotlinx.html.js.div +import kotlinx.html.js.input +import kotlinx.html.js.label +import org.w3c.dom.Element +import kotlin.dom.clear + +fun Element.threeOutputConfig(output: ThreeOutput) { + clear() + append { + card("Layers"){ + div("row") { + (0..11).forEach { layer -> + div("col-1") { + label { +layer.toString() } + input(type = InputType.checkBox).apply { + if (layer == 0) { + checked = true + } + onchange = { + if (checked) { + output.camera.layers.enable(layer) + } else { + output.camera.layers.disable(layer) + } + } + } + } + } + } + } + } +} \ 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 new file mode 100644 index 00000000..d718127e --- /dev/null +++ b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/editor/propertyEditor.kt @@ -0,0 +1,63 @@ +package hep.dataforge.vis.spatial.editor + +import hep.dataforge.io.toJson +import hep.dataforge.meta.* +import hep.dataforge.vis.common.VisualObject +import hep.dataforge.vis.common.findStyle +import hep.dataforge.vis.jsObject +import hep.dataforge.vis.spatial.Material3D.Companion.COLOR_KEY +import hep.dataforge.vis.spatial.Material3D.Companion.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 +import org.w3c.dom.Element +import kotlin.dom.clear + +//FIXME something rotten in JS-Meta converter +fun Meta.toDynamic() = JSON.parse(toJson().toString()) + + +fun Element.propertyEditor(item: VisualObject?, name: String?) { + clear() + if (item != null) { + append { + card("Properties") { + val config = (item.properties ?: item.prototype?.properties) ?: EmptyMeta + val metaToEdit = config.builder().apply { + VISIBLE_KEY to (item.visible ?: true) + COLOR_KEY to (item.color ?: "#ffffff") + OPACITY_KEY to (item.opacity ?: 1.0) + } + val dMeta: dynamic = metaToEdit.toDynamic() + val options: JSONEditorOptions = jsObject { + mode = "form" + onChangeJSON = { item.config.update(DynamicMeta(it.asDynamic())) } + } + JSONEditor(div(), options, dMeta) + } + + val styles = item.styles + if (styles.isNotEmpty()) { + card("Styles") { + item.styles.forEach { style -> + val styleMeta = item.findStyle(style) + h4("container") { +style.toString() } + if (styleMeta != null) { + div("container").apply { + val options: JSONEditorOptions = jsObject { + mode = "view" + } + JSONEditor(this, options, styleMeta.toDynamic()) + } + } + } + } + } + } + } +} diff --git a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/Materials.kt b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/Materials.kt index e6c72174..8e575169 100644 --- a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/Materials.kt +++ b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/Materials.kt @@ -4,6 +4,7 @@ import hep.dataforge.meta.* import hep.dataforge.values.ValueType import hep.dataforge.vis.common.Colors import hep.dataforge.vis.spatial.Material3D +import info.laht.threekt.materials.LineBasicMaterial import info.laht.threekt.materials.Material import info.laht.threekt.materials.MeshBasicMaterial import info.laht.threekt.materials.MeshPhongMaterial @@ -13,8 +14,37 @@ import info.laht.threekt.math.Color object Materials { val DEFAULT_COLOR = Color(Colors.darkgreen) val DEFAULT = MeshPhongMaterial().apply { - this.color.set(DEFAULT_COLOR) + color.set(DEFAULT_COLOR) } + val DEFAULT_LINE_COLOR = Color(Colors.black) + val DEFAULT_LINE = LineBasicMaterial().apply { + color.set(DEFAULT_LINE_COLOR) + } + + + private val materialCache = HashMap() + private val lineMaterialCache = HashMap() + + fun getMaterial(meta: Meta): Material = materialCache.getOrPut(meta) { + MeshBasicMaterial().apply { + color = meta["color"]?.color() ?: DEFAULT_COLOR + opacity = meta["opacity"]?.double ?: 1.0 + transparent = meta["transparent"].boolean ?: (opacity < 1.0) + //node["specularColor"]?.let { specular = it.color() } + //side = 2 + } + } + + fun getLineMaterial(meta: Meta): Material = lineMaterialCache.getOrPut(meta) { + LineBasicMaterial().apply { + color = meta["color"]?.color() ?: DEFAULT_LINE_COLOR + opacity = meta["opacity"].double ?: 1.0 + transparent = meta["transparent"].boolean ?: (opacity < 1.0) + linewidth = meta["thickness"].double ?: 1.0 + } + } + + } /** @@ -41,26 +71,26 @@ fun MetaItem<*>.color(): Color { } } -private val materialCache = HashMap() - /** * Infer Three material based on meta item */ fun Meta?.jsMaterial(): Material { return if (this == null) { Materials.DEFAULT - } else - //TODO add more options for material - return materialCache.getOrPut(this) { - MeshBasicMaterial().apply { - color = get("color")?.color() ?: Materials.DEFAULT_COLOR - opacity = get("opacity")?.double ?: 1.0 - transparent = get("transparent").boolean ?: (opacity < 1.0) - //node["specularColor"]?.let { specular = it.color() } - //side = 2 - } - } + } else { + Materials.getMaterial(this) + } } -fun Material3D?.jsMaterial(): Material = this?.config.jsMaterial() +fun Meta?.jsLineMaterial(): Material { + return if (this == null) { + Materials.DEFAULT_LINE + } else{ + Materials.getLineMaterial(this) + } +} + + +fun Material3D?.jsMaterial(): Material = this?.config.jsMaterial() +fun Material3D?.jsLineMaterial(): Material = this?.config.jsLineMaterial() 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 new file mode 100644 index 00000000..2b0c5d21 --- /dev/null +++ b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/MeshThreeFactory.kt @@ -0,0 +1,99 @@ +package hep.dataforge.vis.spatial.three + +import hep.dataforge.meta.boolean +import hep.dataforge.meta.node +import hep.dataforge.names.asName +import hep.dataforge.names.plus +import hep.dataforge.names.startsWith +import hep.dataforge.vis.spatial.Material3D +import hep.dataforge.vis.spatial.VisualObject3D +import hep.dataforge.vis.spatial.layer +import hep.dataforge.vis.spatial.material +import info.laht.threekt.core.BufferGeometry +import info.laht.threekt.geometries.EdgesGeometry +import info.laht.threekt.geometries.WireframeGeometry +import info.laht.threekt.objects.LineSegments +import info.laht.threekt.objects.Mesh +import kotlin.reflect.KClass + +/** + * Basic geometry-based factory + */ +abstract class MeshThreeFactory( + override val type: KClass +) : ThreeFactory { + /** + * Build a geometry for an object + */ + abstract fun buildGeometry(obj: T): BufferGeometry + + private fun Mesh.applyEdges(obj: T) { + children.find { it.name == "edges" }?.let { remove(it) } + //inherited edges definition, enabled by default + if (obj.getProperty(EDGES_ENABLED_KEY).boolean != false) { + val material = obj.getProperty(EDGES_MATERIAL_KEY).node.jsLineMaterial() + add( + LineSegments( + EdgesGeometry(geometry as BufferGeometry), + material + ) + ) + } + } + + private fun Mesh.applyWireFrame(obj: T) { + children.find { it.name == "wireframe" }?.let { remove(it) } + //inherited wireframe definition, disabled by default + if (obj.getProperty(WIREFRAME_ENABLED_KEY).boolean == true) { + val material = obj.getProperty(WIREFRAME_MATERIAL_KEY).node.jsLineMaterial() + add( + LineSegments( + WireframeGeometry(geometry as BufferGeometry), + material + ) + ) + } + } + + override fun invoke(obj: T): Mesh { + //TODO add caching for geometries using templates + val geometry = buildGeometry(obj) + + //JS sometimes tries to pass Geometry as BufferGeometry + @Suppress("USELESS_IS_CHECK") if (geometry !is BufferGeometry) error("BufferGeometry expected") + + val mesh = Mesh(geometry, obj.material.jsMaterial()).apply { + matrixAutoUpdate = false + applyEdges(obj) + applyWireFrame(obj) + //set position for mesh + updatePosition(obj) + + layers.enable(obj.layer) + children.forEach { + it.layers.enable(obj.layer) + } + } + + //add listener to object properties + obj.onPropertyChange(this) { name, _, _ -> + when { + name.startsWith(VisualObject3D.GEOMETRY_KEY) -> mesh.geometry = buildGeometry(obj) + name.startsWith(WIREFRAME_KEY) -> mesh.applyWireFrame(obj) + name.startsWith(EDGES_KEY) -> mesh.applyEdges(obj) + else -> mesh.updateProperty(obj, name) + } + } + return mesh + } + + companion object { + val EDGES_KEY = "edges".asName() + val WIREFRAME_KEY = "wireframe".asName() + val ENABLED_KEY = "enabled".asName() + val EDGES_ENABLED_KEY = EDGES_KEY + ENABLED_KEY + val EDGES_MATERIAL_KEY = EDGES_KEY + Material3D.MATERIAL_KEY + val WIREFRAME_ENABLED_KEY = WIREFRAME_KEY + ENABLED_KEY + val WIREFRAME_MATERIAL_KEY = WIREFRAME_KEY + Material3D.MATERIAL_KEY + } +} \ No newline at end of file diff --git a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeFactory.kt b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeFactory.kt index c190f048..94e71f5e 100644 --- a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeFactory.kt +++ b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeFactory.kt @@ -1,22 +1,14 @@ package hep.dataforge.vis.spatial.three -import hep.dataforge.meta.boolean -import hep.dataforge.meta.node import hep.dataforge.names.Name -import hep.dataforge.names.asName -import hep.dataforge.names.plus import hep.dataforge.names.startsWith import hep.dataforge.provider.Type import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.spatial.* import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_KEY -import hep.dataforge.vis.spatial.VisualObject3D.Companion.GEOMETRY_KEY import hep.dataforge.vis.spatial.three.ThreeFactory.Companion.TYPE import info.laht.threekt.core.BufferGeometry import info.laht.threekt.core.Object3D -import info.laht.threekt.geometries.EdgesGeometry -import info.laht.threekt.geometries.WireframeGeometry -import info.laht.threekt.objects.LineSegments import info.laht.threekt.objects.Mesh import kotlin.reflect.KClass @@ -46,30 +38,6 @@ internal fun Object3D.updatePosition(obj: VisualObject3D) { updateMatrix() } -internal fun Mesh.updateFrom(obj: T) { - matrixAutoUpdate = false - - //inherited edges definition, enabled by default - if (obj.getProperty(MeshThreeFactory.EDGES_ENABLED_KEY).boolean != false) { - val material = obj.getProperty(MeshThreeFactory.EDGES_MATERIAL_KEY).node?.jsMaterial() ?: Materials.DEFAULT - add(LineSegments(EdgesGeometry(geometry as BufferGeometry), material)) - } - - //inherited wireframe definition, disabled by default - if (obj.getProperty(MeshThreeFactory.WIREFRAME_ENABLED_KEY).boolean == true) { - val material = obj.getProperty(MeshThreeFactory.WIREFRAME_MATERIAL_KEY).node?.jsMaterial() ?: Materials.DEFAULT - add(LineSegments(WireframeGeometry(geometry as BufferGeometry), material)) - } - - //set position for mesh - updatePosition(obj) - - layers.enable(obj.layer) - children.forEach { - it.layers.enable(obj.layer) - } -} - /** * Unsafe invocation of a factory */ @@ -82,53 +50,6 @@ operator fun ThreeFactory.invoke(obj: Any): Object3D { } } -/** - * Basic geometry-based factory - */ -abstract class MeshThreeFactory(override val type: KClass) : ThreeFactory { - /** - * Build a geometry for an object - */ - abstract fun buildGeometry(obj: T): BufferGeometry - - - override fun invoke(obj: T): Mesh { - //create mesh from geometry - return buildMesh(obj) { buildGeometry(it) } - } - - companion object { - val EDGES_KEY = "edges".asName() - val WIREFRAME_KEY = "wireframe".asName() - val ENABLED_KEY = "enabled".asName() - val EDGES_ENABLED_KEY = EDGES_KEY + ENABLED_KEY - val EDGES_MATERIAL_KEY = EDGES_KEY + MATERIAL_KEY - val WIREFRAME_ENABLED_KEY = WIREFRAME_KEY + ENABLED_KEY - val WIREFRAME_MATERIAL_KEY = WIREFRAME_KEY + MATERIAL_KEY - - fun buildMesh(obj: T, geometryBuilder: (T) -> BufferGeometry): Mesh { - //TODO add caching for geometries using templates - val geometry = geometryBuilder(obj) - - //JS sometimes tries to pass Geometry as BufferGeometry - @Suppress("USELESS_IS_CHECK") if (geometry !is BufferGeometry) error("BufferGeometry expected") - - val mesh = Mesh(geometry, obj.material.jsMaterial()) - - mesh.updateFrom(obj) - - //add listener to object properties - obj.onPropertyChange(this) { name, _, _ -> - mesh.updateProperty(obj, name) - if (name.startsWith(GEOMETRY_KEY)) { - mesh.geometry = geometryBuilder(obj) - } - } - return mesh - } - } -} - fun Object3D.updateProperty(source: VisualObject, propertyName: Name) { if (this is Mesh && propertyName.startsWith(MATERIAL_KEY)) { //updated material diff --git a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeLineFactory.kt b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeLineFactory.kt new file mode 100644 index 00000000..cb109e8b --- /dev/null +++ b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/three/ThreeLineFactory.kt @@ -0,0 +1,32 @@ +package hep.dataforge.vis.spatial.three + +import hep.dataforge.vis.spatial.PolyLine +import hep.dataforge.vis.spatial.layer +import hep.dataforge.vis.spatial.material +import info.laht.threekt.core.Geometry +import info.laht.threekt.core.Object3D +import info.laht.threekt.objects.LineSegments +import kotlin.reflect.KClass + +object ThreeLineFactory : ThreeFactory { + override val type: KClass get() = PolyLine::class + + override fun invoke(obj: PolyLine): Object3D { + val geometry = Geometry().apply { + vertices = obj.points.toTypedArray() + } + + val material = obj.material.jsLineMaterial() + return LineSegments(geometry, material).apply { + + updatePosition(obj) + layers.enable(obj.layer) + + //add listener to object properties + obj.onPropertyChange(this) { propertyName, _, _ -> + updateProperty(obj, propertyName) + } + } + } + +} \ No newline at end of file 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 db90a39e..be1f7803 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 @@ -33,6 +33,7 @@ class ThreePlugin : AbstractPlugin() { objectFactories[Convex::class] = ThreeConvexFactory objectFactories[Sphere::class] = ThreeSphereFactory objectFactories[ConeSegment::class] = ThreeCylinderFactory + objectFactories[PolyLine::class] = ThreeLineFactory } private fun findObjectFactory(type: KClass): ThreeFactory<*>? { @@ -46,7 +47,7 @@ class ThreePlugin : AbstractPlugin() { is VisualGroup3D -> { val group = ThreeGroup() obj.children.forEach { (name, child) -> - if (child is VisualObject3D) { + if (child is VisualObject3D && child.ignore != true) { try { val object3D = buildObject3D(child) object3D.name = name.toString() diff --git a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/tree/propertyEditor.kt b/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/tree/propertyEditor.kt deleted file mode 100644 index 782ada2b..00000000 --- a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/tree/propertyEditor.kt +++ /dev/null @@ -1,98 +0,0 @@ -package hep.dataforge.vis.spatial.tree - -import hep.dataforge.io.toJson -import hep.dataforge.meta.DynamicMeta -import hep.dataforge.meta.builder -import hep.dataforge.meta.update -import hep.dataforge.vis.common.VisualObject -import hep.dataforge.vis.spatial.Material3D.Companion.COLOR_KEY -import hep.dataforge.vis.spatial.Material3D.Companion.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.InputType -import kotlinx.html.dom.append -import kotlinx.html.js.* -import org.w3c.dom.Element -import kotlin.dom.clear - -fun Element.propertyEditor(item: VisualObject?, name: String?) { - clear() - if (item != null) { - append { - div("card") { - div("card-body") { - h3(classes = "card-title") { - +(name ?: "") - } - form { - div("form-group row") { - label("col-form-label col-4") { - +"Color: " - } - input(InputType.color, classes = "form-control col-8") { - value = item.color ?: "#ffffff" - }.apply { - onInputFunction = { - item.color = value - } - } - } - div("form-group row") { - label("col-form-label col-4") { - +"Opacity: " - } - input(InputType.range, classes = "form-control col-8") { - min = "0.0" - max = "1.0" - step = "0.1" - value = item.opacity.toString() - }.apply { - onInputFunction = { - item.opacity = value.toDouble() - } - } - } - div("form-group row") { - label("col-form-label col-4") { +"Visible: " } - div("col-8") { - div("form-check") { - input(InputType.checkBox, classes = "form-check-input").apply { - this.checked = item.visible ?: true - onInputFunction = { - item.visible = checked - } - } - } - } - - } - } - } - } - (item.properties ?: item.prototype?.properties)?.let { config -> - div("card") { - div("card-body") { - h3(classes = "card-title") { +"Properties" } - }.apply { - val metaToEdit = config.builder().apply { - VISIBLE_KEY to (item.visible ?: true) - COLOR_KEY to (item.color ?: "#ffffff") - OPACITY_KEY to (item.opacity ?: 1.0) - } - //FIXME something rotten in JS-Meta converter - val jsObject: dynamic = JSON.parse(metaToEdit.toJson().toString()) - //jsObject.material.color != null - val options = (js("{}") as JSONEditorOptions).apply { - mode = "form" - onChangeJSON = { item.config.update(DynamicMeta(it.asDynamic())) } - } - JSONEditor(this, options, jsObject) - } - } - } - } - } -} \ No newline at end of file diff --git a/dataforge-vis-spatial/src/jsMain/kotlin/info/laht/threekt/objects/LineSegments.kt b/dataforge-vis-spatial/src/jsMain/kotlin/info/laht/threekt/objects/LineSegments.kt index e682cdc7..3925c7fb 100644 --- a/dataforge-vis-spatial/src/jsMain/kotlin/info/laht/threekt/objects/LineSegments.kt +++ b/dataforge-vis-spatial/src/jsMain/kotlin/info/laht/threekt/objects/LineSegments.kt @@ -28,7 +28,10 @@ package info.laht.threekt.objects import info.laht.threekt.core.BufferGeometry +import info.laht.threekt.core.Geometry import info.laht.threekt.core.Object3D import info.laht.threekt.materials.Material -open external class LineSegments(geometry: BufferGeometry, material: Material) : Object3D \ No newline at end of file +open external class LineSegments(geometry: BufferGeometry, material: Material) : Object3D { + constructor(geometry: Geometry, material: Material) +} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b0acbdcd..7c4388a9 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 8e25e6c1..83f2acfd 100644 --- a/gradlew +++ b/gradlew @@ -125,8 +125,8 @@ if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` JAVACMD=`cygpath --unix "$JAVACMD"` diff --git a/settings.gradle.kts b/settings.gradle.kts index 1c7412a3..b49736d1 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -5,6 +5,7 @@ pluginManagement { gradlePluginPortal() maven("https://kotlin.bintray.com/kotlinx") maven("https://dl.bintray.com/kotlin/kotlin-eap") + maven("https://dl.bintray.com/mipt-npm/dataforge") maven("https://dl.bintray.com/mipt-npm/scientifik") maven("https://dl.bintray.com/mipt-npm/dev") } @@ -17,7 +18,6 @@ pluginManagement { "org.jetbrains.kotlin.jvm" -> useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:${requested.version}") "org.jetbrains.kotlin.js" -> useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:${requested.version}") "kotlin-dce-js" -> useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:${requested.version}") - "kotlin2js" -> useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:${requested.version}") "org.jetbrains.kotlin.frontend" -> useModule("org.jetbrains.kotlin:kotlin-frontend-plugin:${requested.version}") "scientifik.mpp", "scientifik.publish", "scientifik.jvm", "scientifik.js" -> useModule("scientifik:gradle-tools:${requested.version}") "org.openjfx.javafxplugin" -> useModule("org.openjfx:javafx-plugin:${requested.version}") @@ -32,6 +32,7 @@ rootProject.name = "dataforge-vis" include( ":dataforge-vis-common", + ":wrappers", ":dataforge-vis-fx", ":dataforge-vis-spatial", ":dataforge-vis-spatial-gdml", 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 index 496ec8fe..a35bf4c9 100644 --- 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 @@ -1,10 +1,10 @@ package hep.dataforge.vis.spatial.demo import hep.dataforge.context.ContextBuilder +import hep.dataforge.vis.ApplicationBase import hep.dataforge.vis.common.Colors -import hep.dataforge.vis.hmr.ApplicationBase -import hep.dataforge.vis.hmr.startApplication import hep.dataforge.vis.spatial.* +import hep.dataforge.vis.startApplication import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.delay import kotlinx.coroutines.isActive @@ -130,6 +130,21 @@ private class ThreeDemoApp : ApplicationBase() { } } } + 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 + } + } + } } diff --git a/wrappers/build.gradle.kts b/wrappers/build.gradle.kts new file mode 100644 index 00000000..177713d0 --- /dev/null +++ b/wrappers/build.gradle.kts @@ -0,0 +1,23 @@ +plugins { + id("scientifik.js") + //id("kotlin-dce-js") +} + +dependencies { + api(project(":dataforge-vis-common")) + + testCompile(kotlin("test-js")) +} + +kotlin{ + sourceSets["main"].apply{ + dependencies{ + api(npm("style-loader")) + api(npm("inspire-tree","6.0.1")) + api(npm("inspire-tree-dom","4.0.6")) + api(npm("jsoneditor")) + api(npm("dat.gui")) + //api("org.jetbrains:kotlin-extensions:1.0.1-pre.83-kotlin-1.3.50") + } + } +} diff --git a/wrappers/src/main/kotlin/dat.kt b/wrappers/src/main/kotlin/dat.kt new file mode 100644 index 00000000..7c56c20a --- /dev/null +++ b/wrappers/src/main/kotlin/dat.kt @@ -0,0 +1,102 @@ +@file:Suppress( + "INTERFACE_WITH_SUPERCLASS", + "OVERRIDING_FINAL_MEMBER", + "RETURN_TYPE_MISMATCH_ON_OVERRIDE", + "CONFLICTING_OVERLOADS", + "EXTERNAL_DELEGATION" +) +@file:JsModule("dat.gui") +@file:JsNonModule + +package hep.dataforge.vis.spatial.editor + +import org.w3c.dom.HTMLElement + +external interface GUIParams { + var autoPlace: Boolean? get() = definedExternally; set(value) = definedExternally + var closed: Boolean? get() = definedExternally; set(value) = definedExternally + var closeOnTop: Boolean? get() = definedExternally; set(value) = definedExternally + var hideable: Boolean? get() = definedExternally; set(value) = definedExternally + var load: Any? get() = definedExternally; set(value) = definedExternally + var name: String? get() = definedExternally; set(value) = definedExternally + var preset: String? get() = definedExternally; set(value) = definedExternally + var width: Number? get() = definedExternally; set(value) = definedExternally +} + +external open class GUI(option: GUIParams? = definedExternally /* null */) { + open var __controllers: Array + open var __folders: Array + open var domElement: HTMLElement + open fun add( + target: Any, + propName: String, + min: Number? = definedExternally /* null */, + max: Number? = definedExternally /* null */, + step: Number? = definedExternally /* null */ + ): GUIController + + open fun add(target: Any, propName: String, status: Boolean): GUIController + open fun add(target: Any, propName: String, items: Array): GUIController + open fun add(target: Any, propName: String, items: Array): GUIController + open fun add(target: Any, propName: String, items: Any): GUIController + open fun addColor(target: Any, propName: String): GUIController + open fun remove(controller: GUIController) + open fun destroy() + open fun addFolder(propName: String): GUI + open fun removeFolder(subFolder: GUI) + open fun open() + open fun close() + open fun hide() + open fun show() + open fun remember(target: Any, vararg additionalTargets: Any) + open fun getRoot(): GUI + open fun getSaveObject(): Any + open fun save() + open fun saveAs(presetName: String) + open fun revert(gui: GUI) + open fun listen(controller: GUIController) + open fun updateDisplay() + open var parent: GUI + open var scrollable: Boolean + open var autoPlace: Boolean + open var preset: String + open var width: Number + open var name: String + open var closed: Boolean + open var load: Any + open var useLocalStorage: Boolean + + companion object { + var CLASS_AUTO_PLACE: String + var CLASS_AUTO_PLACE_CONTAINER: String + var CLASS_MAIN: String + var CLASS_CONTROLLER_ROW: String + var CLASS_TOO_TALL: String + var CLASS_CLOSED: String + var CLASS_CLOSE_BUTTON: String + var CLASS_CLOSE_TOP: String + var CLASS_CLOSE_BOTTOM: String + var CLASS_DRAG: String + var DEFAULT_WIDTH: Number + var TEXT_CLOSED: String + var TEXT_OPEN: String + } +} + +external open class GUIController { + open fun destroy() + open var onChange: (value: Any? /* = null */) -> GUIController + open var onFinishChange: (value: Any? /* = null */) -> GUIController + open fun setValue(value: Any): GUIController + open fun getValue(): Any + open fun updateDisplay(): GUIController + open fun isModified(): Boolean + open fun min(n: Number): GUIController + open fun max(n: Number): GUIController + open fun step(n: Number): GUIController + open fun fire(): GUIController + open fun options(option: Any): GUIController + open fun name(s: String): GUIController + open fun listen(): GUIController + open fun remove(): GUIController +} \ No newline at end of file diff --git a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/tree/eventemitter2.kt b/wrappers/src/main/kotlin/eventemitter2.kt similarity index 98% rename from dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/tree/eventemitter2.kt rename to wrappers/src/main/kotlin/eventemitter2.kt index 2d6e2e50..36b925f2 100644 --- a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/tree/eventemitter2.kt +++ b/wrappers/src/main/kotlin/eventemitter2.kt @@ -8,7 +8,7 @@ @file:JsModule("eventemitter2") @file: JsNonModule -package hep.dataforge.vis.spatial.tree +package hep.dataforge.vis.spatial.editor import kotlin.js.Promise diff --git a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/tree/inspire-tree-dom.kt b/wrappers/src/main/kotlin/inspire-tree-dom.kt similarity index 92% rename from dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/tree/inspire-tree-dom.kt rename to wrappers/src/main/kotlin/inspire-tree-dom.kt index 2e595adf..f3e00d08 100644 --- a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/tree/inspire-tree-dom.kt +++ b/wrappers/src/main/kotlin/inspire-tree-dom.kt @@ -6,7 +6,7 @@ "EXTERNAL_DELEGATION" ) -package hep.dataforge.vis.spatial.tree +package hep.dataforge.vis.spatial.editor import org.w3c.dom.HTMLElement diff --git a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/tree/inspire-tree.kt b/wrappers/src/main/kotlin/inspire-tree.kt similarity index 99% rename from dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/tree/inspire-tree.kt rename to wrappers/src/main/kotlin/inspire-tree.kt index c19df4de..74887be7 100644 --- a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/tree/inspire-tree.kt +++ b/wrappers/src/main/kotlin/inspire-tree.kt @@ -7,7 +7,7 @@ "unused" ) -package hep.dataforge.vis.spatial.tree +package hep.dataforge.vis.spatial.editor import kotlin.js.Promise import kotlin.js.RegExp diff --git a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/tree/jsonEditor.kt b/wrappers/src/main/kotlin/jsoneditor.kt similarity index 99% rename from dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/tree/jsonEditor.kt rename to wrappers/src/main/kotlin/jsoneditor.kt index 1e3abd3c..a83f4b87 100644 --- a/dataforge-vis-spatial/src/jsMain/kotlin/hep/dataforge/vis/spatial/tree/jsonEditor.kt +++ b/wrappers/src/main/kotlin/jsoneditor.kt @@ -6,7 +6,7 @@ "EXTERNAL_DELEGATION" ) -package hep.dataforge.vis.spatial.tree +package hep.dataforge.vis.spatial.editor import org.w3c.dom.HTMLElement