Fixed style display

This commit is contained in:
Alexander Nozik 2019-10-09 09:34:20 +03:00
parent 47c1a30c89
commit 9f157a80b9
30 changed files with 525 additions and 283 deletions

View File

@ -19,6 +19,8 @@ kotlin {
dependencies { dependencies {
api("hep.dataforge:dataforge-output-html:$dataforgeVersion") api("hep.dataforge:dataforge-output-html:$dataforgeVersion")
api(npm("text-encoding")) api(npm("text-encoding"))
api("org.jetbrains:kotlin-extensions:1.0.1-pre.83-kotlin-1.3.50")
api(npm("core-js"))
} }
} }
} }

View File

@ -19,14 +19,17 @@ abstract class AbstractVisualGroup : AbstractVisualObject(), MutableVisualGroup
*/ */
abstract override val children: Map<NameToken, VisualObject> //get() = _children abstract override val children: Map<NameToken, VisualObject> //get() = _children
//TODO replace by custom object with get/set functionality /**
protected abstract val styles: MutableMap<Name, Meta> * 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? = styles[name] override fun getStyle(name: Name): Meta? = styleSheet[name]
override fun setStyle(name: Name, meta: Meta) { override fun addStyle(name: Name, meta: Meta) {
fun VisualObject.applyStyle(name: Name, meta: Meta) { fun VisualObject.applyStyle(name: Name, meta: Meta) {
if (style.contains(name.toString())) { if (styles.contains(name)) {
//full update //full update
//TODO do a fine grained update //TODO do a fine grained update
if (this is AbstractVisualObject) { if (this is AbstractVisualObject) {
@ -41,7 +44,7 @@ abstract class AbstractVisualGroup : AbstractVisualObject(), MutableVisualGroup
} }
} }
} }
styles[name] = meta styleSheet[name] = meta
applyStyle(name, meta) applyStyle(name, meta)
} }

View File

@ -19,10 +19,10 @@ abstract class AbstractVisualObject : VisualObject {
abstract override var properties: Config? abstract override var properties: Config?
override var style: List<String> override var styles: List<Name>
get() = properties?.let { it[STYLE_KEY].stringList } ?: emptyList() get() = properties?.get(STYLE_KEY).stringList.map(String::toName)
set(value) { set(value) {
setProperty(STYLE_KEY, value) setProperty(STYLE_KEY, value.map { it.toString() })
styleChanged() styleChanged()
} }
@ -61,8 +61,10 @@ abstract class AbstractVisualObject : VisualObject {
/** /**
* Collect all styles for this object in a laminate * Collect all styles for this object in a laminate
*/ */
protected val appliedStyles: Meta protected val mergedStyles: Meta
get() = styleCache ?: Laminate(style.map { it.toName() }.mapNotNull(::findStyle)).merge().also { styleCache = it } get() = styleCache ?: findAllStyles().merge().also {
styleCache = it
}
/** /**
@ -75,9 +77,9 @@ abstract class AbstractVisualObject : VisualObject {
override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? { override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? {
return if (inherit) { return if (inherit) {
properties?.get(name) ?: appliedStyles[name] ?: parent?.getProperty(name, inherit) properties?.get(name) ?: mergedStyles[name] ?: parent?.getProperty(name, inherit)
} else { } else {
properties?.get(name) ?: appliedStyles[name] properties?.get(name) ?: mergedStyles[name]
} }
} }
@ -90,7 +92,7 @@ abstract class AbstractVisualObject : VisualObject {
} }
} }
internal fun VisualObject.findStyle(styleName: Name): Meta? { fun VisualObject.findStyle(styleName: Name): Meta? {
if (this is VisualGroup) { if (this is VisualGroup) {
val style = getStyle(styleName) val style = getStyle(styleName)
if (style != null) return style if (style != null) return style

View File

@ -41,7 +41,7 @@ interface VisualGroup : Provider, Iterable<VisualObject>, VisualObject {
/** /**
* Add or replace style with given name * Add or replace style with given name
*/ */
fun setStyle(name: Name, meta: Meta) fun addStyle(name: Name, meta: Meta)
operator fun get(name: Name): VisualObject? { operator fun get(name: Name): VisualObject? {
return when { return when {

View File

@ -1,9 +1,6 @@
package hep.dataforge.vis.common package hep.dataforge.vis.common
import hep.dataforge.meta.Config import hep.dataforge.meta.*
import hep.dataforge.meta.Configurable
import hep.dataforge.meta.MetaItem
import hep.dataforge.meta.MetaRepr
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.asName import hep.dataforge.names.asName
import hep.dataforge.names.toName import hep.dataforge.names.toName
@ -56,11 +53,16 @@ interface VisualObject : MetaRepr, Configurable {
*/ */
fun removeChangeListener(owner: Any?) fun removeChangeListener(owner: Any?)
var style: List<String> /**
* List of names of styles applied to this object
*/
var styles: List<Name>
fun findAllStyles(): Laminate = Laminate(styles.distinct().mapNotNull(::findStyle))
companion object { companion object {
const val TYPE = "visual" const val TYPE = "visual"
val STYLE_KEY = "style".asName() val STYLE_KEY = "@style".asName()
//const val META_KEY = "@meta" //const val META_KEY = "@meta"
//const val TAGS_KEY = "@tags" //const val TAGS_KEY = "@tags"
@ -71,5 +73,5 @@ fun VisualObject.getProperty(key: String, inherit: Boolean = true): MetaItem<*>?
fun VisualObject.setProperty(key: String, value: Any?) = setProperty(key.toName(), value) fun VisualObject.setProperty(key: String, value: Any?) = setProperty(key.toName(), value)
fun VisualObject.applyStyle(name: String) { fun VisualObject.applyStyle(name: String) {
style = style + name styles = styles + name.toName()
} }

View File

@ -0,0 +1,23 @@
package hep.dataforge.vis.hmr
import kotlinext.js.objectAssign
inline fun <T : Any> 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 <T : Any> clone(obj: T) = objectAssign(jsObject<T> {}, obj)
//inline fun <T : Any> 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
}
}

View File

@ -14,7 +14,6 @@ import hep.dataforge.vis.spatial.VisualGroup3D
import hep.dataforge.vis.spatial.VisualObject3D import hep.dataforge.vis.spatial.VisualObject3D
import hep.dataforge.vis.spatial.rotationOrder import hep.dataforge.vis.spatial.rotationOrder
import scientifik.gdml.* import scientifik.gdml.*
import kotlin.collections.set
import kotlin.random.Random import kotlin.random.Random
class GDMLTransformer(val root: GDML) { class GDMLTransformer(val root: GDML) {
@ -31,7 +30,7 @@ class GDMLTransformer(val root: GDML) {
* A special group for local templates * A special group for local templates
*/ */
val templates by lazy { VisualGroup3D() } val templates by lazy { VisualGroup3D() }
private val styles = HashMap<Name, Meta>() private val styleCache = HashMap<Name, Meta>()
var lUnit: LUnit = LUnit.MM var lUnit: LUnit = LUnit.MM
@ -42,7 +41,7 @@ class GDMLTransformer(val root: GDML) {
var solidConfiguration: VisualObject3D.(parent: GDMLVolume, solid: GDMLSolid) -> Unit = { _, _ -> } var solidConfiguration: VisualObject3D.(parent: GDMLVolume, solid: GDMLSolid) -> Unit = { _, _ -> }
fun VisualObject.useStyle(name: String, builder: MetaBuilder.() -> Unit) { fun VisualObject.useStyle(name: String, builder: MetaBuilder.() -> Unit) {
styles.getOrPut(name.toName()){ styleCache.getOrPut(name.toName()){
buildMeta(builder) buildMeta(builder)
} }
applyStyle(name) applyStyle(name)
@ -60,35 +59,17 @@ class GDMLTransformer(val root: GDML) {
obj.solidConfiguration(parent, solid) obj.solidConfiguration(parent, solid)
} }
//
fun printStatistics() { // internal fun solidAdded(solid: GDMLSolid) {
println("Solids:") // solidCounter[solid.name] = (solidCounter[solid.name] ?: 0) + 1
solidCounter.entries.sortedByDescending { it.value }.forEach { // }
println("\t$it")
}
println("Solids total: ${solidCounter.values.sum()}")
}
private val solidCounter = HashMap<String, Int>()
internal fun solidAdded(solid: GDMLSolid) {
solidCounter[solid.name] = (solidCounter[solid.name] ?: 0) + 1
}
var onFinish: GDMLTransformer.() -> Unit = {} var onFinish: GDMLTransformer.() -> Unit = {}
var optimizeSingleChild = false
//var optimizations: List<GDMLOptimization> = emptyList()
internal fun finalize(final: VisualGroup3D): VisualGroup3D { internal fun finalize(final: VisualGroup3D): VisualGroup3D {
// var res = final
// optimizations.forEach {
// res = it(res)
// }
final.templates = templates final.templates = templates
styles.forEach { styleCache.forEach {
final.setStyle(it.key, it.value) final.addStyle(it.key, it.value)
} }
final.rotationOrder = RotationOrder.ZXY final.rotationOrder = RotationOrder.ZXY
onFinish(this@GDMLTransformer) onFinish(this@GDMLTransformer)

View File

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

View File

@ -47,7 +47,7 @@ private fun VisualGroup3D.addSolid(
name: String = "", name: String = "",
block: VisualObject3D.() -> Unit = {} block: VisualObject3D.() -> Unit = {}
): VisualObject3D { ): VisualObject3D {
context.solidAdded(solid) //context.solidAdded(solid)
val lScale = solid.lscale(context.lUnit) val lScale = solid.lscale(context.lUnit)
val aScale = solid.ascale() val aScale = solid.ascale()
return when (solid) { return when (solid) {
@ -150,31 +150,13 @@ private fun VisualGroup3D.addPhysicalVolume(
when (context.volumeAction(volume)) { when (context.volumeAction(volume)) {
GDMLTransformer.Action.ACCEPT -> { GDMLTransformer.Action.ACCEPT -> {
val group = volume(context, volume) val group = volume(context, volume)
//optimizing single child case this[physVolume.name ?: ""] = group.apply {
if (context.optimizeSingleChild && group.children.size == 1) { withPosition(
this[physVolume.name ?: ""] = group.children.values.first().apply { context.lUnit,
//Must set this to avoid parent reset error physVolume.resolvePosition(context.root),
parent = null physVolume.resolveRotation(context.root),
//setting offset from physical volume physVolume.resolveScale(context.root)
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)
)
}
} }
} }
GDMLTransformer.Action.CACHE -> { GDMLTransformer.Action.CACHE -> {

View File

@ -15,9 +15,12 @@ import hep.dataforge.vis.spatial.gdml.toVisual
import hep.dataforge.vis.spatial.three.ThreeOutput import hep.dataforge.vis.spatial.three.ThreeOutput
import hep.dataforge.vis.spatial.three.ThreePlugin import hep.dataforge.vis.spatial.three.ThreePlugin
import hep.dataforge.vis.spatial.three.output import hep.dataforge.vis.spatial.three.output
import hep.dataforge.vis.spatial.tree.propertyEditor import hep.dataforge.vis.spatial.transform.RemoveSingleChild
import hep.dataforge.vis.spatial.tree.render import hep.dataforge.vis.spatial.transform.UnRef
import hep.dataforge.vis.spatial.tree.toTree import hep.dataforge.vis.spatial.transform.transformInPlace
import hep.dataforge.vis.spatial.editor.propertyEditor
import hep.dataforge.vis.spatial.editor.render
import hep.dataforge.vis.spatial.editor.toTree
import kotlinx.html.InputType import kotlinx.html.InputType
import kotlinx.html.dom.append import kotlinx.html.dom.append
import kotlinx.html.js.input import kotlinx.html.js.input
@ -62,7 +65,7 @@ private class GDMLDemoApp : ApplicationBase() {
val string = result as String val string = result as String
// try { // try {
block(file.name, string) block(file.name, string)
// } catch (ex: Exception) { // } catch (ex: Exception) {
// console.error(ex) // console.error(ex)
// } // }
@ -129,23 +132,24 @@ private class GDMLDemoApp : ApplicationBase() {
volumeAction = { volume -> volumeAction = { volume ->
when { when {
volume.name.startsWith("ecal01lay") -> GDMLTransformer.Action.REJECT volume.name.startsWith("ecal01lay") -> GDMLTransformer.Action.REJECT
volume.name.startsWith("ecal") -> GDMLTransformer.Action.CACHE // volume.name.startsWith("ecal") -> GDMLTransformer.Action.CACHE
volume.name.startsWith("UPBL") -> GDMLTransformer.Action.REJECT volume.name.startsWith("UPBL") -> GDMLTransformer.Action.REJECT
volume.name.startsWith("USCL") -> GDMLTransformer.Action.REJECT volume.name.startsWith("USCL") -> GDMLTransformer.Action.REJECT
volume.name.startsWith("U") -> GDMLTransformer.Action.CACHE // volume.name.startsWith("U") -> GDMLTransformer.Action.CACHE
volume.name.startsWith("VPBL") -> GDMLTransformer.Action.REJECT volume.name.startsWith("VPBL") -> GDMLTransformer.Action.REJECT
volume.name.startsWith("VSCL") -> GDMLTransformer.Action.REJECT volume.name.startsWith("VSCL") -> GDMLTransformer.Action.REJECT
volume.name.startsWith("V") -> GDMLTransformer.Action.CACHE // volume.name.startsWith("V") -> GDMLTransformer.Action.CACHE
else -> GDMLTransformer.Action.ACCEPT else -> GDMLTransformer.Action.CACHE
} }
} }
solidConfiguration = { parent, solid -> solidConfiguration = { parent, solid ->
if (parent.physVolumes.isNotEmpty() if (
|| solid.name.startsWith("Coil") solid.name.startsWith("Coil")
|| solid.name.startsWith("Yoke") || solid.name.startsWith("Yoke")
|| solid.name.startsWith("Magnet") || solid.name.startsWith("Magnet")
|| solid.name.startsWith("Pole") || solid.name.startsWith("Pole")
|| parent.physVolumes.isNotEmpty()
) { ) {
useStyle("opaque") { useStyle("opaque") {
OPACITY_KEY to 0.3 OPACITY_KEY to 0.3
@ -185,6 +189,9 @@ private class GDMLDemoApp : ApplicationBase() {
} }
} }
//Optimize tree
//(visual as? VisualGroup3D)?.transformInPlace(UnRef, RemoveSingleChild)
message("Rendering") message("Rendering")
val output = three.output(canvas as HTMLElement) val output = three.output(canvas as HTMLElement)

View File

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

View File

@ -1,21 +1,30 @@
package hep.dataforge.vis.spatial.gdml package hep.dataforge.vis.spatial.gdml
import hep.dataforge.vis.spatial.Material3D
import hep.dataforge.vis.spatial.Visual3DPlugin import hep.dataforge.vis.spatial.Visual3DPlugin
import hep.dataforge.vis.spatial.VisualGroup3D import hep.dataforge.vis.spatial.VisualGroup3D
import hep.dataforge.vis.spatial.opacity 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 nl.adaptivity.xmlutil.StAXReader
import scientifik.gdml.GDML import scientifik.gdml.GDML
import java.io.File import java.io.File
fun main() { 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\\BM@N.gdml")
//val file = File("D:\\Work\\Projects\\gdml.kt\\gdml-source\\cubes.gdml")
val xmlReader = StAXReader(file.inputStream(), "UTF-8") val xmlReader = StAXReader(file.inputStream(), "UTF-8")
val xml = GDML.format.parse(GDML.serializer(), xmlReader) val xml = GDML.format.parse(GDML.serializer(), xmlReader)
val visual = xml.toVisual { val visual = xml.toVisual {
lUnit = LUnit.CM lUnit = LUnit.CM
volumeAction = { volume ->
when {
volume.name.startsWith("ecal01lay") -> GDMLTransformer.Action.REJECT
else -> GDMLTransformer.Action.CACHE
}
}
solidConfiguration = { parent, solid -> solidConfiguration = { parent, solid ->
if (parent.physVolumes.isNotEmpty() if (parent.physVolumes.isNotEmpty()
|| solid.name.startsWith("Coil") || solid.name.startsWith("Coil")
@ -24,16 +33,14 @@ fun main() {
|| solid.name.startsWith("Pole") || solid.name.startsWith("Pole")
) { ) {
useStyle("opaque") { 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 string = Visual3DPlugin.json.stringify(VisualGroup3D.serializer(), visual)
val tmpFile = File.createTempFile("dataforge-visual", ".json") val tmpFile = File.createTempFile("dataforge-visual", ".json")

View File

@ -27,14 +27,9 @@ kotlin {
} }
jsMain { jsMain {
dependencies { dependencies {
api(project(":wrappers"))
implementation(npm("three", "0.106.2")) implementation(npm("three", "0.106.2"))
implementation(npm("@hi-level/three-csg", "1.0.6")) 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"))
} }
} }
} }

View File

@ -5,14 +5,8 @@ package hep.dataforge.vis.spatial
import hep.dataforge.io.ConfigSerializer import hep.dataforge.io.ConfigSerializer
import hep.dataforge.io.NameSerializer import hep.dataforge.io.NameSerializer
import hep.dataforge.meta.* import hep.dataforge.meta.*
import hep.dataforge.names.Name import hep.dataforge.names.*
import hep.dataforge.names.NameToken import hep.dataforge.vis.common.*
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 kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers import kotlinx.serialization.UseSerializers
import kotlin.collections.component1 import kotlin.collections.component1
@ -35,13 +29,14 @@ class Proxy(val templateName: Name) : AbstractVisualObject(), VisualGroup, Visua
/** /**
* Recursively search for defined template in the parent * Recursively search for defined template in the parent
*/ */
val prototype: VisualObject3D get() = (parent as? VisualGroup3D)?.getTemplate(templateName) val prototype: VisualObject3D
?: error("Template with name $templateName not found in $parent") 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 getStyle(name: Name): Meta? = (parent as VisualGroup?)?.getStyle(name)
override fun setStyle(name: Name, meta: Meta) { override fun addStyle(name: Name, meta: Meta) {
(parent as VisualGroup?)?.setStyle(name, meta) (parent as VisualGroup?)?.addStyle(name, meta)
//do nothing //do nothing
} }
@ -74,6 +69,15 @@ class Proxy(val templateName: Name) : AbstractVisualObject(), VisualGroup, Visua
?: error("Prototype with name $name not found in ${this@Proxy}") ?: error("Prototype with name $name not found in ${this@Proxy}")
override var styles: List<Name>
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 { inner class ProxyChild(val name: Name) : AbstractVisualObject(), VisualGroup {
val prototype: VisualObject by lazy { val prototype: VisualObject by lazy {
@ -89,8 +93,8 @@ class Proxy(val templateName: Name) : AbstractVisualObject(), VisualGroup, Visua
override fun getStyle(name: Name): Meta? = this@Proxy.getStyle(name) override fun getStyle(name: Name): Meta? = this@Proxy.getStyle(name)
override fun setStyle(name: Name, meta: Meta) { override fun addStyle(name: Name, meta: Meta) {
this@Proxy.setStyle(name, meta) this@Proxy.addStyle(name, meta)
} }
override var properties: Config? override var properties: Config?
@ -113,12 +117,12 @@ class Proxy(val templateName: Name) : AbstractVisualObject(), VisualGroup, Visua
override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? { override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? {
return if (inherit) { return if (inherit) {
properties?.get(name) properties?.get(name)
?: appliedStyles[name] ?: mergedStyles[name]
?: parent?.getProperty(name, inherit) ?: parent?.getProperty(name, inherit)
?: prototype.getProperty(name, inherit) ?: prototype.getProperty(name, inherit)
} else { } else {
properties?.get(name) properties?.get(name)
?: appliedStyles[name] ?: mergedStyles[name]
?: prototype.getProperty(name, inherit) ?: prototype.getProperty(name, inherit)
} }
} }
@ -141,4 +145,4 @@ inline fun VisualGroup3D.ref(
templateName: Name, templateName: Name,
name: String = "", name: String = "",
action: Proxy.() -> Unit = {} action: Proxy.() -> Unit = {}
) = Proxy(templateName).apply(action).also { set(name, it) } ) = Proxy(templateName).apply(action).also { set(name, it) }

View File

@ -40,7 +40,7 @@ class VisualGroup3D : AbstractVisualGroup(), VisualObject3D {
//FIXME to be lifted to AbstractVisualGroup after https://github.com/Kotlin/kotlinx.serialization/issues/378 is fixed //FIXME to be lifted to AbstractVisualGroup after https://github.com/Kotlin/kotlinx.serialization/issues/378 is fixed
override var properties: Config? = null override var properties: Config? = null
override val styles = HashMap<Name, Meta>() override val styleSheet = HashMap<Name, Meta>()
override var position: Point3D? = null override var position: Point3D? = null
override var rotation: Point3D? = null override var rotation: Point3D? = null

View File

@ -10,6 +10,7 @@ import hep.dataforge.output.Output
import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.common.VisualObject
import hep.dataforge.vis.spatial.VisualObject3D.Companion.DETAIL_KEY 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.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.SELECTED_KEY
import hep.dataforge.vis.spatial.VisualObject3D.Companion.VISIBLE_KEY import hep.dataforge.vis.spatial.VisualObject3D.Companion.VISIBLE_KEY
import kotlinx.serialization.UseSerializers import kotlinx.serialization.UseSerializers
@ -37,6 +38,7 @@ interface VisualObject3D : VisualObject {
val SELECTED_KEY = "selected".asName() val SELECTED_KEY = "selected".asName()
val DETAIL_KEY = "detail".asName() val DETAIL_KEY = "detail".asName()
val LAYER_KEY = "layer".asName() val LAYER_KEY = "layer".asName()
val IGNORE_KEY = "ignore".asName()
val GEOMETRY_KEY = "geometey".asName() val GEOMETRY_KEY = "geometey".asName()
@ -108,6 +110,14 @@ var VisualObject.visible: Boolean?
get() = getProperty(VISIBLE_KEY).boolean get() = getProperty(VISIBLE_KEY).boolean
set(value) = setProperty(VISIBLE_KEY, value) 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? var VisualObject.selected: Boolean?
get() = getProperty(SELECTED_KEY).boolean get() = getProperty(SELECTED_KEY).boolean
set(value) = setProperty(SELECTED_KEY, value) set(value) = setProperty(SELECTED_KEY, value)

View File

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

View File

@ -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<VisualGroup3D>() {
private fun VisualGroup.countRefs(): Map<Name, Int> {
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<MutableVisualGroup>().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()
}
}

View File

@ -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<T : VisualObject> {
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 : VisualObject> T.transform(vararg transform: VisualTreeTransform<T>): T {
var res = this
transform.forEach {
res = it(res)
}
return res
}
fun <T : VisualObject> T.transformInPlace(vararg transform: VisualTreeTransform<in T>) {
transform.forEach { it(this) }
}

View File

@ -1,6 +1,6 @@
@file:Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE") @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.meta.string
import hep.dataforge.names.EmptyName import hep.dataforge.names.EmptyName
@ -69,12 +69,14 @@ fun VisualGroup.toTree(onFocus: (VisualObject?, String?) -> Unit = { _, _ -> }):
fun TreeNode.fillChildren(group: VisualGroup, groupName: Name) { fun TreeNode.fillChildren(group: VisualGroup, groupName: Name) {
group.children.forEach { (token, obj) -> group.children.forEach { (token, obj) ->
val name = groupName + token if(! token.body.startsWith("@")) {
val nodeConfig = generateNodeConfig(obj, name) val name = groupName + token
val childNode = addChild(nodeConfig) val nodeConfig = generateNodeConfig(obj, name)
map[childNode.id] = obj val childNode = addChild(nodeConfig)
if (obj is VisualGroup) { map[childNode.id] = obj
childNode.fillChildren(obj, name) if (obj is VisualGroup) {
childNode.fillChildren(obj, name)
}
} }
} }
} }

View File

@ -0,0 +1,71 @@
package hep.dataforge.vis.spatial.editor
import hep.dataforge.io.toJson
import hep.dataforge.meta.*
import hep.dataforge.names.toName
import hep.dataforge.vis.common.VisualObject
import hep.dataforge.vis.common.findStyle
import hep.dataforge.vis.hmr.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.h3
import kotlinx.html.js.h4
import org.w3c.dom.Element
import kotlin.dom.clear
fun Meta.toDynamic() = JSON.parse<dynamic>(toJson().toString())
fun Element.propertyEditor(item: VisualObject?, name: String?) {
clear()
if (item != null) {
append {
div("card") {
div("card-body") {
h3(classes = "card-title") { +"Properties" }
}.apply {
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)
}
//FIXME something rotten in JS-Meta converter
val dMeta: dynamic = metaToEdit.toDynamic()
//jsObject.material.color != null
val options: JSONEditorOptions = jsObject{
mode = "form"
onChangeJSON = { item.config.update(DynamicMeta(it.asDynamic())) }
}
JSONEditor(this, options, dMeta)
}
}
div("card") {
div("card-body") {
h3(classes = "card-title") { +"Styles" }
}
item.styles.forEach { style ->
val styleMeta = item.findStyle(style)
h4 { +style.toString() }
if (styleMeta != null) {
div("container").apply {
val options: JSONEditorOptions = jsObject{
mode = "view"
}
JSONEditor(this, options, styleMeta.toDynamic())
}
}
}
}
}
}
}

View File

@ -46,7 +46,7 @@ class ThreePlugin : AbstractPlugin() {
is VisualGroup3D -> { is VisualGroup3D -> {
val group = ThreeGroup() val group = ThreeGroup()
obj.children.forEach { (name, child) -> obj.children.forEach { (name, child) ->
if (child is VisualObject3D) { if (child is VisualObject3D && child.ignore != true) {
try { try {
val object3D = buildObject3D(child) val object3D = buildObject3D(child)
object3D.name = name.toString() object3D.name = name.toString()

View File

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

View File

@ -32,6 +32,7 @@ rootProject.name = "dataforge-vis"
include( include(
":dataforge-vis-common", ":dataforge-vis-common",
":wrappers",
":dataforge-vis-fx", ":dataforge-vis-fx",
":dataforge-vis-spatial", ":dataforge-vis-spatial",
":dataforge-vis-spatial-gdml", ":dataforge-vis-spatial-gdml",

23
wrappers/build.gradle.kts Normal file
View File

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

View File

@ -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<GUIController>
open var __folders: Array<GUI>
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<String>): GUIController
open fun add(target: Any, propName: String, items: Array<Number>): 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
}

View File

@ -8,7 +8,7 @@
@file:JsModule("eventemitter2") @file:JsModule("eventemitter2")
@file: JsNonModule @file: JsNonModule
package hep.dataforge.vis.spatial.tree package hep.dataforge.vis.spatial.editor
import kotlin.js.Promise import kotlin.js.Promise

View File

@ -6,7 +6,7 @@
"EXTERNAL_DELEGATION" "EXTERNAL_DELEGATION"
) )
package hep.dataforge.vis.spatial.tree package hep.dataforge.vis.spatial.editor
import org.w3c.dom.HTMLElement import org.w3c.dom.HTMLElement

View File

@ -7,7 +7,7 @@
"unused" "unused"
) )
package hep.dataforge.vis.spatial.tree package hep.dataforge.vis.spatial.editor
import kotlin.js.Promise import kotlin.js.Promise
import kotlin.js.RegExp import kotlin.js.RegExp

View File

@ -6,7 +6,7 @@
"EXTERNAL_DELEGATION" "EXTERNAL_DELEGATION"
) )
package hep.dataforge.vis.spatial.tree package hep.dataforge.vis.spatial.editor
import org.w3c.dom.HTMLElement import org.w3c.dom.HTMLElement