forked from kscience/visionforge
Merge branch 'dev' of https://github.com/mipt-npm/dataforge-vis into doc
Conflicts: dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/AbstractVisualObject.kt
This commit is contained in:
commit
7f0b08fcc9
@ -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"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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, apply: Boolean) {
|
||||||
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,8 +44,10 @@ abstract class AbstractVisualGroup : AbstractVisualObject(), MutableVisualGroup
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
styles[name] = meta
|
styleSheet[name] = meta
|
||||||
applyStyle(name, meta)
|
if (apply) {
|
||||||
|
applyStyle(name, meta)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -12,9 +12,6 @@ internal data class PropertyListener(
|
|||||||
val action: (name: Name, oldItem: MetaItem<*>?, newItem: MetaItem<*>?) -> Unit
|
val action: (name: Name, oldItem: MetaItem<*>?, newItem: MetaItem<*>?) -> Unit
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
|
||||||
* Abstract implementation of [VisualObject]
|
|
||||||
*/
|
|
||||||
abstract class AbstractVisualObject : VisualObject {
|
abstract class AbstractVisualObject : VisualObject {
|
||||||
|
|
||||||
@Transient
|
@Transient
|
||||||
@ -22,13 +19,10 @@ abstract class AbstractVisualObject : VisualObject {
|
|||||||
|
|
||||||
abstract override var properties: Config?
|
abstract override var properties: Config?
|
||||||
|
|
||||||
/**
|
override var styles: List<Name>
|
||||||
* Style(s) of the object
|
get() = properties?.get(STYLE_KEY).stringList.map(String::toName)
|
||||||
*/
|
|
||||||
override var style: List<String>
|
|
||||||
get() = properties?.let { it[STYLE_KEY].stringList } ?: emptyList()
|
|
||||||
set(value) {
|
set(value) {
|
||||||
setProperty(STYLE_KEY, value)
|
setProperty(STYLE_KEY, value.map { it.toString() })
|
||||||
styleChanged()
|
styleChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,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
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -81,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]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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) {
|
if (this is VisualGroup) {
|
||||||
val style = getStyle(styleName)
|
val style = getStyle(styleName)
|
||||||
if (style != null) return style
|
if (style != null) return style
|
||||||
|
@ -44,7 +44,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, apply: Boolean = true)
|
||||||
|
|
||||||
operator fun get(name: Name): VisualObject? {
|
operator fun get(name: Name): VisualObject? {
|
||||||
return when {
|
return when {
|
||||||
|
@ -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"
|
||||||
@ -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
|
* Apply style to [VisualObject] by adding it to the [style] list
|
||||||
*/
|
*/
|
||||||
fun VisualObject.applyStyle(name: String) {
|
fun VisualObject.applyStyle(name: String) {
|
||||||
style = style + name
|
styles = styles + name.toName()
|
||||||
}
|
}
|
@ -4,6 +4,7 @@ import hep.dataforge.meta.*
|
|||||||
import hep.dataforge.names.Name
|
import hep.dataforge.names.Name
|
||||||
import hep.dataforge.names.NameToken
|
import hep.dataforge.names.NameToken
|
||||||
import hep.dataforge.names.asName
|
import hep.dataforge.names.asName
|
||||||
|
import hep.dataforge.names.toName
|
||||||
import hep.dataforge.values.Value
|
import hep.dataforge.values.Value
|
||||||
import kotlin.jvm.JvmName
|
import kotlin.jvm.JvmName
|
||||||
import kotlin.properties.ReadOnlyProperty
|
import kotlin.properties.ReadOnlyProperty
|
||||||
@ -35,81 +36,79 @@ class VisualObjectDelegate(
|
|||||||
}
|
}
|
||||||
|
|
||||||
class VisualObjectDelegateWrapper<T>(
|
class VisualObjectDelegateWrapper<T>(
|
||||||
|
val obj: VisualObject,
|
||||||
val key: Name?,
|
val key: Name?,
|
||||||
val default: T,
|
val default: T,
|
||||||
val inherited: Boolean,
|
val inherited: Boolean,
|
||||||
val write: Config.(name: Name, value: T) -> Unit = { name, value -> set(name, value) },
|
val write: Config.(name: Name, value: T) -> Unit = { name, value -> set(name, value) },
|
||||||
val read: (MetaItem<*>?) -> T?
|
val read: (MetaItem<*>?) -> T?
|
||||||
) : ReadWriteProperty<VisualObject, T> {
|
) : ReadWriteProperty<Any?, T> {
|
||||||
|
|
||||||
//private var cachedName: Name? = null
|
//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()
|
val name = key ?: property.name.asName()
|
||||||
return if (inherited) {
|
return read(obj.getProperty(name,inherited))?:default
|
||||||
read(thisRef.getProperty(name))
|
|
||||||
} else {
|
|
||||||
read(thisRef.config[name])
|
|
||||||
} ?: 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()
|
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) =
|
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) =
|
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) =
|
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) =
|
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) =
|
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) =
|
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) =
|
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) =
|
fun VisualObject.item(key: String? = null, inherited: Boolean = true) =
|
||||||
VisualObjectDelegateWrapper(key?.asName(), null, inherited) { it }
|
VisualObjectDelegateWrapper(this, key?.toName(), null, inherited) { it }
|
||||||
|
|
||||||
//fun <T : Configurable> Configurable.spec(spec: Specification<T>, key: String? = null) = ChildConfigDelegate<T>(key) { spec.wrap(this) }
|
//fun <T : Configurable> Configurable.spec(spec: Specification<T>, key: String? = null) = ChildConfigDelegate<T>(key) { spec.wrap(this) }
|
||||||
|
|
||||||
@JvmName("safeString")
|
@JvmName("safeString")
|
||||||
fun VisualObject.string(default: String, key: String? = null, inherited: Boolean = false) =
|
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")
|
@JvmName("safeBoolean")
|
||||||
fun VisualObject.boolean(default: Boolean, key: String? = null, inherited: Boolean = false) =
|
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")
|
@JvmName("safeNumber")
|
||||||
fun VisualObject.number(default: Number, key: String? = null, inherited: Boolean = false) =
|
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")
|
@JvmName("safeDouble")
|
||||||
fun VisualObject.double(default: Double, key: String? = null, inherited: Boolean = false) =
|
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")
|
@JvmName("safeInt")
|
||||||
fun VisualObject.int(default: Int, key: String? = null, inherited: Boolean = false) =
|
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 <reified E : Enum<E>> VisualObject.enum(default: E, key: String? = null, inherited: Boolean = false) =
|
inline fun <reified E : Enum<E>> VisualObject.enum(default: E, key: String? = null, inherited: Boolean = false) =
|
||||||
VisualObjectDelegateWrapper(
|
VisualObjectDelegateWrapper(
|
||||||
|
this,
|
||||||
key?.let { NameToken(it).asName() },
|
key?.let { NameToken(it).asName() },
|
||||||
default,
|
default,
|
||||||
inherited
|
inherited
|
||||||
@ -121,11 +120,11 @@ fun <T> VisualObject.merge(
|
|||||||
key: String? = null,
|
key: String? = null,
|
||||||
transformer: (Sequence<MetaItem<*>>) -> T
|
transformer: (Sequence<MetaItem<*>>) -> T
|
||||||
): ReadOnlyProperty<VisualObject, T> {
|
): ReadOnlyProperty<VisualObject, T> {
|
||||||
return object : ReadOnlyProperty<VisualObject, T> {
|
return object : ReadOnlyProperty<Any?, T> {
|
||||||
override fun getValue(thisRef: VisualObject, property: KProperty<*>): T {
|
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
|
||||||
val name = key?.asName() ?: property.name.asName()
|
val name = key?.toName() ?: property.name.asName()
|
||||||
val sequence = sequence<MetaItem<*>> {
|
val sequence = sequence<MetaItem<*>> {
|
||||||
var thisObj: VisualObject? = thisRef
|
var thisObj: VisualObject? = this@merge
|
||||||
while (thisObj != null) {
|
while (thisObj != null) {
|
||||||
thisObj.config[name]?.let { yield(it) }
|
thisObj.config[name]?.let { yield(it) }
|
||||||
thisObj = thisObj.parent
|
thisObj = thisObj.parent
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package hep.dataforge.vis.hmr
|
package hep.dataforge.vis
|
||||||
|
|
||||||
import kotlin.browser.document
|
import kotlin.browser.document
|
||||||
import kotlin.dom.hasClass
|
import kotlin.dom.hasClass
|
@ -0,0 +1,21 @@
|
|||||||
|
package hep.dataforge.vis
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
@ -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, false)
|
||||||
}
|
}
|
||||||
final.rotationOrder = RotationOrder.ZXY
|
final.rotationOrder = RotationOrder.ZXY
|
||||||
onFinish(this@GDMLTransformer)
|
onFinish(this@GDMLTransformer)
|
||||||
|
@ -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
|
|
||||||
}
|
|
@ -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 -> {
|
||||||
|
@ -1,33 +1,26 @@
|
|||||||
package hep.dataforge.vis.spatial.gdml.demo
|
package hep.dataforge.vis.spatial.gdml.demo
|
||||||
|
|
||||||
import hep.dataforge.context.Global
|
import hep.dataforge.context.Global
|
||||||
import hep.dataforge.vis.common.VisualGroup
|
import hep.dataforge.vis.ApplicationBase
|
||||||
import hep.dataforge.vis.hmr.ApplicationBase
|
|
||||||
import hep.dataforge.vis.hmr.startApplication
|
|
||||||
import hep.dataforge.vis.spatial.Material3D.Companion.OPACITY_KEY
|
import hep.dataforge.vis.spatial.Material3D.Companion.OPACITY_KEY
|
||||||
import hep.dataforge.vis.spatial.Visual3DPlugin
|
import hep.dataforge.vis.spatial.Visual3DPlugin
|
||||||
import hep.dataforge.vis.spatial.VisualGroup3D
|
import hep.dataforge.vis.spatial.VisualGroup3D
|
||||||
import hep.dataforge.vis.spatial.VisualObject3D
|
import hep.dataforge.vis.spatial.VisualObject3D
|
||||||
import hep.dataforge.vis.spatial.attachChildren
|
import hep.dataforge.vis.spatial.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.GDMLTransformer
|
||||||
import hep.dataforge.vis.spatial.gdml.LUnit
|
import hep.dataforge.vis.spatial.gdml.LUnit
|
||||||
import hep.dataforge.vis.spatial.gdml.toVisual
|
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.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.startApplication
|
||||||
import hep.dataforge.vis.spatial.tree.render
|
|
||||||
import hep.dataforge.vis.spatial.tree.toTree
|
|
||||||
import kotlinx.html.InputType
|
|
||||||
import kotlinx.html.dom.append
|
import kotlinx.html.dom.append
|
||||||
import kotlinx.html.js.input
|
|
||||||
import kotlinx.html.js.li
|
|
||||||
import kotlinx.html.js.p
|
import kotlinx.html.js.p
|
||||||
import kotlinx.html.js.ul
|
import org.w3c.dom.DragEvent
|
||||||
import org.w3c.dom.Element
|
|
||||||
import org.w3c.dom.HTMLDivElement
|
import org.w3c.dom.HTMLDivElement
|
||||||
import org.w3c.dom.HTMLElement
|
import org.w3c.dom.HTMLElement
|
||||||
import org.w3c.dom.events.Event
|
|
||||||
import org.w3c.files.FileList
|
import org.w3c.files.FileList
|
||||||
import org.w3c.files.FileReader
|
import org.w3c.files.FileReader
|
||||||
import org.w3c.files.get
|
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/
|
* 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.stopPropagation()
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
event.asDynamic().dataTransfer.dropEffect = "copy"
|
event.dataTransfer?.dropEffect = "copy"
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load data from text file
|
* 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.stopPropagation()
|
||||||
event.preventDefault()
|
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")
|
?: throw RuntimeException("Failed to load file")
|
||||||
|
|
||||||
FileReader().apply {
|
FileReader().apply {
|
||||||
onload = {
|
onload = {
|
||||||
val string = result as String
|
val string = result as String
|
||||||
|
block(file.name, string)
|
||||||
// try {
|
|
||||||
block(file.name, string)
|
|
||||||
// } catch (ex: Exception) {
|
|
||||||
// console.error(ex)
|
|
||||||
// }
|
|
||||||
|
|
||||||
}
|
}
|
||||||
readAsText(file)
|
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 = {
|
private val gdmlConfiguration: GDMLTransformer.() -> Unit = {
|
||||||
lUnit = LUnit.CM
|
lUnit = LUnit.CM
|
||||||
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("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("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
|
else -> GDMLTransformer.Action.CACHE
|
||||||
else -> GDMLTransformer.Action.ACCEPT
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
solidConfiguration = { parent, solid ->
|
solidConfiguration = { parent, solid ->
|
||||||
if (parent.physVolumes.isNotEmpty()
|
if (
|
||||||
|| solid.name.startsWith("Coil")
|
solid.name.startsWith("Yoke")
|
||||||
|| solid.name.startsWith("Yoke")
|
|
||||||
|| 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,18 +143,17 @@ private class GDMLDemoApp : ApplicationBase() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Optimize tree
|
||||||
|
//(visual as? VisualGroup3D)?.transformInPlace(UnRef, RemoveSingleChild)
|
||||||
|
|
||||||
message("Rendering")
|
message("Rendering")
|
||||||
val output = three.output(canvas as HTMLElement)
|
|
||||||
|
|
||||||
//output.camera.layers.enable(1)
|
//output.camera.layers.enable(1)
|
||||||
output.camera.layers.set(0)
|
val output = three.output(canvas as HTMLElement)
|
||||||
setupLayers(layers, output)
|
|
||||||
|
|
||||||
if (visual is VisualGroup) {
|
output.camera.layers.set(0)
|
||||||
visual.toTree(editor::propertyEditor).render(tree as HTMLElement) {
|
layers.threeOutputConfig(output)
|
||||||
showCheckboxes = false
|
tree.visualObjectTree(visual, editor::propertyEditor)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
output.render(visual)
|
output.render(visual)
|
||||||
message(null)
|
message(null)
|
||||||
@ -204,8 +161,8 @@ private class GDMLDemoApp : ApplicationBase() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
(document.getElementById("drop_zone") as? HTMLDivElement)?.apply {
|
(document.getElementById("drop_zone") as? HTMLDivElement)?.apply {
|
||||||
addEventListener("dragover", { handleDragOver(it) }, false)
|
addEventListener("dragover", { handleDragOver(it as DragEvent) }, false)
|
||||||
addEventListener("drop", { loadData(it, action) }, false)
|
addEventListener("drop", { loadData(it as DragEvent, action) }, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -29,45 +29,12 @@
|
|||||||
|
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-9" id="canvas"></div>
|
<div class="col-lg-3" id="tree"></div>
|
||||||
<div class="col-3">
|
<div class="col-lg-6">
|
||||||
|
<div class="row" id="layers"></div>
|
||||||
<div id="editor"></div>
|
<div class="row container" id="canvas"></div>
|
||||||
<div class="accordion" id="accordion">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header" id="layers-header">
|
|
||||||
<h2 class="mb-0">
|
|
||||||
<button class="btn btn-link" type="button" data-toggle="collapse" data-target="#layers-body"
|
|
||||||
aria-expanded="true" aria-controls="layers-body">
|
|
||||||
Layers
|
|
||||||
</button>
|
|
||||||
</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="layers-body" class="collapse show" aria-labelledby="layers-header"
|
|
||||||
data-parent="#accordion">
|
|
||||||
<div class="card-body">
|
|
||||||
<div id="layers"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header" id="tree-header">
|
|
||||||
<h2 class="mb-0">
|
|
||||||
<button class="btn btn-link collapsed" type="button" data-toggle="collapse"
|
|
||||||
data-target="#tree-body" aria-expanded="false" aria-controls="tree-body">
|
|
||||||
Object tree
|
|
||||||
</button>
|
|
||||||
</h2>
|
|
||||||
</div>
|
|
||||||
<div id="tree-body" class="collapse" aria-labelledby="tree-header" data-parent="#accordion">
|
|
||||||
<div class="card-body">
|
|
||||||
<div id="tree"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-lg-3" id="editor"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
}
|
@ -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")
|
||||||
|
@ -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"))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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<Point3D>) : 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) }
|
@ -35,21 +35,27 @@ 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, apply: Boolean) {
|
||||||
(parent as VisualGroup?)?.setStyle(name, meta)
|
(parent as VisualGroup?)?.addStyle(name, meta, apply)
|
||||||
//do nothing
|
//do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? {
|
override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? {
|
||||||
return if (inherit) {
|
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 {
|
} 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}")
|
?: 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 +104,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, apply: Boolean) {
|
||||||
this@Proxy.setStyle(name, meta)
|
this@Proxy.addStyle(name, meta, apply)
|
||||||
}
|
}
|
||||||
|
|
||||||
override var properties: Config?
|
override var properties: Config?
|
||||||
@ -113,12 +128,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)
|
|
||||||
?: prototype.getProperty(name, inherit)
|
?: prototype.getProperty(name, inherit)
|
||||||
|
?: parent?.getProperty(name, inherit)
|
||||||
} else {
|
} else {
|
||||||
properties?.get(name)
|
properties?.get(name)
|
||||||
?: appliedStyles[name]
|
?: mergedStyles[name]
|
||||||
?: prototype.getProperty(name, inherit)
|
?: prototype.getProperty(name, inherit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
//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
|
||||||
|
@ -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
|
||||||
@ -40,6 +41,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()
|
||||||
|
|
||||||
@ -111,6 +113,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)
|
||||||
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
@ -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()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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) }
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
package hep.dataforge.vis.spatial
|
package hep.dataforge.vis.spatial
|
||||||
|
|
||||||
import hep.dataforge.context.Global
|
import hep.dataforge.vis.spatial.Visual3DPlugin.Companion.json
|
||||||
import kotlinx.serialization.ImplicitReflectionSerializer
|
import kotlinx.serialization.ImplicitReflectionSerializer
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
@ -12,9 +12,9 @@ class SerializationTest {
|
|||||||
val cube = Box(100f,100f,100f).apply{
|
val cube = Box(100f,100f,100f).apply{
|
||||||
color(222)
|
color(222)
|
||||||
}
|
}
|
||||||
val meta = cube.toMeta()
|
val string = json.stringify(Box.serializer(),cube)
|
||||||
println(meta)
|
println(string)
|
||||||
val newCube = Box(Global,null, meta)
|
val newCube = json.parse(Box.serializer(),string)
|
||||||
assertEquals(cube.toMeta(),newCube.toMeta())
|
assertEquals(cube.toMeta(),newCube.toMeta())
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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<HTMLElement>.card(title: String, crossinline block: TagConsumer<HTMLElement>.() -> Unit) {
|
||||||
|
div("card w-100") {
|
||||||
|
div("card-body") {
|
||||||
|
h3(classes = "card-title") { +title }
|
||||||
|
block()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
@ -9,21 +9,19 @@ import hep.dataforge.names.NameToken
|
|||||||
import hep.dataforge.vis.common.VisualGroup
|
import hep.dataforge.vis.common.VisualGroup
|
||||||
import hep.dataforge.vis.common.VisualObject
|
import hep.dataforge.vis.common.VisualObject
|
||||||
import hep.dataforge.vis.common.getProperty
|
import hep.dataforge.vis.common.getProperty
|
||||||
|
import hep.dataforge.vis.jsObject
|
||||||
import hep.dataforge.vis.spatial.Proxy
|
import hep.dataforge.vis.spatial.Proxy
|
||||||
import hep.dataforge.vis.spatial.visible
|
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
|
import kotlin.js.json
|
||||||
|
|
||||||
operator fun Name.plus(other: NameToken): Name = Name(tokens + other)
|
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(
|
val config = (json(
|
||||||
"checkbox" to json(
|
"checkbox" to json(
|
||||||
"autoCheckChildren" to false
|
"autoCheckChildren" to false
|
||||||
@ -32,7 +30,7 @@ internal fun createInspireTree(block: Config.() -> Unit = {}): InspireTree {
|
|||||||
return InspireTree(config)
|
return InspireTree(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun VisualGroup.toTree(onFocus: (VisualObject?, String?) -> Unit = { _, _ -> }): InspireTree {
|
private fun VisualObject.toTree(onFocus: (VisualObject?, String?) -> Unit = { _, _ -> }): InspireTree {
|
||||||
|
|
||||||
val map = HashMap<String, VisualObject>()
|
val map = HashMap<String, VisualObject>()
|
||||||
|
|
||||||
@ -67,14 +65,18 @@ fun VisualGroup.toTree(onFocus: (VisualObject?, String?) -> Unit = { _, _ -> }):
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun TreeNode.fillChildren(group: VisualGroup, groupName: Name) {
|
fun TreeNode.fillChildren(group: VisualObject, groupName: Name) {
|
||||||
group.children.forEach { (token, obj) ->
|
if(group is VisualGroup) {
|
||||||
val name = groupName + token
|
group.children.forEach { (token, obj) ->
|
||||||
val nodeConfig = generateNodeConfig(obj, name)
|
if (!token.body.startsWith("@")) {
|
||||||
val childNode = addChild(nodeConfig)
|
val name = groupName + token
|
||||||
map[childNode.id] = obj
|
val nodeConfig = generateNodeConfig(obj, name)
|
||||||
if (obj is VisualGroup) {
|
val childNode = addChild(nodeConfig)
|
||||||
childNode.fillChildren(obj, name)
|
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
|
return inspireTree
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Element.visualObjectTree(group: VisualObject, onFocus: (VisualObject?, String?) -> Unit) {
|
||||||
|
clear()
|
||||||
|
append {
|
||||||
|
card("Visual object tree") {
|
||||||
|
val domConfig = jsObject<DomConfig> {
|
||||||
|
target = div()
|
||||||
|
showCheckboxes = false
|
||||||
|
}
|
||||||
|
InspireTreeDOM(group.toTree(onFocus), domConfig)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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<dynamic>(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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,7 @@ import hep.dataforge.meta.*
|
|||||||
import hep.dataforge.values.ValueType
|
import hep.dataforge.values.ValueType
|
||||||
import hep.dataforge.vis.common.Colors
|
import hep.dataforge.vis.common.Colors
|
||||||
import hep.dataforge.vis.spatial.Material3D
|
import hep.dataforge.vis.spatial.Material3D
|
||||||
|
import info.laht.threekt.materials.LineBasicMaterial
|
||||||
import info.laht.threekt.materials.Material
|
import info.laht.threekt.materials.Material
|
||||||
import info.laht.threekt.materials.MeshBasicMaterial
|
import info.laht.threekt.materials.MeshBasicMaterial
|
||||||
import info.laht.threekt.materials.MeshPhongMaterial
|
import info.laht.threekt.materials.MeshPhongMaterial
|
||||||
@ -13,8 +14,37 @@ import info.laht.threekt.math.Color
|
|||||||
object Materials {
|
object Materials {
|
||||||
val DEFAULT_COLOR = Color(Colors.darkgreen)
|
val DEFAULT_COLOR = Color(Colors.darkgreen)
|
||||||
val DEFAULT = MeshPhongMaterial().apply {
|
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<Meta, Material>()
|
||||||
|
private val lineMaterialCache = HashMap<Meta, Material>()
|
||||||
|
|
||||||
|
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<Meta, Material>()
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Infer Three material based on meta item
|
* Infer Three material based on meta item
|
||||||
*/
|
*/
|
||||||
fun Meta?.jsMaterial(): Material {
|
fun Meta?.jsMaterial(): Material {
|
||||||
return if (this == null) {
|
return if (this == null) {
|
||||||
Materials.DEFAULT
|
Materials.DEFAULT
|
||||||
} else
|
} else {
|
||||||
//TODO add more options for material
|
Materials.getMaterial(this)
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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()
|
||||||
|
|
||||||
|
@ -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<T : VisualObject3D>(
|
||||||
|
override val type: KClass<out T>
|
||||||
|
) : ThreeFactory<T> {
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
}
|
||||||
|
}
|
@ -1,22 +1,14 @@
|
|||||||
package hep.dataforge.vis.spatial.three
|
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.Name
|
||||||
import hep.dataforge.names.asName
|
|
||||||
import hep.dataforge.names.plus
|
|
||||||
import hep.dataforge.names.startsWith
|
import hep.dataforge.names.startsWith
|
||||||
import hep.dataforge.provider.Type
|
import hep.dataforge.provider.Type
|
||||||
import hep.dataforge.vis.common.VisualObject
|
import hep.dataforge.vis.common.VisualObject
|
||||||
import hep.dataforge.vis.spatial.*
|
import hep.dataforge.vis.spatial.*
|
||||||
import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_KEY
|
import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_KEY
|
||||||
import hep.dataforge.vis.spatial.VisualObject3D.Companion.GEOMETRY_KEY
|
|
||||||
import hep.dataforge.vis.spatial.three.ThreeFactory.Companion.TYPE
|
import hep.dataforge.vis.spatial.three.ThreeFactory.Companion.TYPE
|
||||||
import info.laht.threekt.core.BufferGeometry
|
import info.laht.threekt.core.BufferGeometry
|
||||||
import info.laht.threekt.core.Object3D
|
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 info.laht.threekt.objects.Mesh
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
@ -46,30 +38,6 @@ internal fun Object3D.updatePosition(obj: VisualObject3D) {
|
|||||||
updateMatrix()
|
updateMatrix()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun <T : VisualObject3D> 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
|
* Unsafe invocation of a factory
|
||||||
*/
|
*/
|
||||||
@ -82,53 +50,6 @@ operator fun <T : VisualObject3D> ThreeFactory<T>.invoke(obj: Any): Object3D {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Basic geometry-based factory
|
|
||||||
*/
|
|
||||||
abstract class MeshThreeFactory<T : VisualObject3D>(override val type: KClass<out T>) : ThreeFactory<T> {
|
|
||||||
/**
|
|
||||||
* 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 <T : VisualObject3D> 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) {
|
fun Object3D.updateProperty(source: VisualObject, propertyName: Name) {
|
||||||
if (this is Mesh && propertyName.startsWith(MATERIAL_KEY)) {
|
if (this is Mesh && propertyName.startsWith(MATERIAL_KEY)) {
|
||||||
//updated material
|
//updated material
|
||||||
|
@ -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<PolyLine> {
|
||||||
|
override val type: KClass<out PolyLine> 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -33,6 +33,7 @@ class ThreePlugin : AbstractPlugin() {
|
|||||||
objectFactories[Convex::class] = ThreeConvexFactory
|
objectFactories[Convex::class] = ThreeConvexFactory
|
||||||
objectFactories[Sphere::class] = ThreeSphereFactory
|
objectFactories[Sphere::class] = ThreeSphereFactory
|
||||||
objectFactories[ConeSegment::class] = ThreeCylinderFactory
|
objectFactories[ConeSegment::class] = ThreeCylinderFactory
|
||||||
|
objectFactories[PolyLine::class] = ThreeLineFactory
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun findObjectFactory(type: KClass<out VisualObject3D>): ThreeFactory<*>? {
|
private fun findObjectFactory(type: KClass<out VisualObject3D>): ThreeFactory<*>? {
|
||||||
@ -46,7 +47,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()
|
||||||
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -28,7 +28,10 @@
|
|||||||
package info.laht.threekt.objects
|
package info.laht.threekt.objects
|
||||||
|
|
||||||
import info.laht.threekt.core.BufferGeometry
|
import info.laht.threekt.core.BufferGeometry
|
||||||
|
import info.laht.threekt.core.Geometry
|
||||||
import info.laht.threekt.core.Object3D
|
import info.laht.threekt.core.Object3D
|
||||||
import info.laht.threekt.materials.Material
|
import info.laht.threekt.materials.Material
|
||||||
|
|
||||||
open external class LineSegments(geometry: BufferGeometry, material: Material) : Object3D
|
open external class LineSegments(geometry: BufferGeometry, material: Material) : Object3D {
|
||||||
|
constructor(geometry: Geometry, material: Material)
|
||||||
|
}
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,5 +1,5 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
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
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
4
gradlew
vendored
4
gradlew
vendored
@ -125,8 +125,8 @@ if $darwin; then
|
|||||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# For Cygwin, switch paths to Windows format before running java
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
if $cygwin ; then
|
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
|
@ -5,6 +5,7 @@ pluginManagement {
|
|||||||
gradlePluginPortal()
|
gradlePluginPortal()
|
||||||
maven("https://kotlin.bintray.com/kotlinx")
|
maven("https://kotlin.bintray.com/kotlinx")
|
||||||
maven("https://dl.bintray.com/kotlin/kotlin-eap")
|
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/scientifik")
|
||||||
maven("https://dl.bintray.com/mipt-npm/dev")
|
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.jvm" -> useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:${requested.version}")
|
||||||
"org.jetbrains.kotlin.js" -> 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}")
|
"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}")
|
"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}")
|
"scientifik.mpp", "scientifik.publish", "scientifik.jvm", "scientifik.js" -> useModule("scientifik:gradle-tools:${requested.version}")
|
||||||
"org.openjfx.javafxplugin" -> useModule("org.openjfx:javafx-plugin:${requested.version}")
|
"org.openjfx.javafxplugin" -> useModule("org.openjfx:javafx-plugin:${requested.version}")
|
||||||
@ -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",
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
package hep.dataforge.vis.spatial.demo
|
package hep.dataforge.vis.spatial.demo
|
||||||
|
|
||||||
import hep.dataforge.context.ContextBuilder
|
import hep.dataforge.context.ContextBuilder
|
||||||
|
import hep.dataforge.vis.ApplicationBase
|
||||||
import hep.dataforge.vis.common.Colors
|
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.spatial.*
|
||||||
|
import hep.dataforge.vis.startApplication
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.isActive
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
23
wrappers/build.gradle.kts
Normal file
23
wrappers/build.gradle.kts
Normal 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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
102
wrappers/src/main/kotlin/dat.kt
Normal file
102
wrappers/src/main/kotlin/dat.kt
Normal 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
|
||||||
|
}
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue
Block a user