v0.2.0-dev-22 #47

Merged
altavir merged 158 commits from dev into master 2021-07-17 11:04:22 +03:00
32 changed files with 135 additions and 144 deletions
Showing only changes of commit 2b7971eeea - Show all commits

View File

@ -73,7 +73,7 @@ The `visionforge-core` module also includes configuration editors for JS (in `js
**Class diagram:**
![](docs/resources/class-diag-core.png)
![](docs/images/class-diag-core.png)
### visionforge-solid
@ -82,7 +82,7 @@ Includes common classes and serializers for 3D visualization, as well as Three.j
**Class diagram:**
![](docs/resources/class-diag-solid.png)
![](docs/images/class-diag-solid.png)
##### Prototypes
@ -127,7 +127,7 @@ Some shapes will also periodically change their color and visibility.
**Example view:**
![](docs/resources/spatial-showcase.png)
![](docs/images/spatial-showcase.png)
### Full-Stack Application Example - Muon Monitor Visualization
@ -139,7 +139,7 @@ A full-stack application example, showing the
**Example view:**
![](docs/resources/muon-monitor.png)
![](docs/images/muon-monitor.png)
### GDML Example
@ -150,7 +150,7 @@ Visualization example for geometry defined as GDML file.
##### Example view:
![](docs/resources/gdml-demo.png)
![](docs/images/gdml-demo.png)
## Thanks and references

View File

@ -12,4 +12,4 @@ drag-and-drop GDML file to the window to see visualization. For an example file,
##### Example view:
![](../../docs/resources/gdml-demo.png)
![](../../docs/images/gdml-demo.png)

View File

@ -30,5 +30,5 @@ run `demo/muon-monitor/application/run` task.
##### Example view:
![](../../docs/resources/muon-monitor.png)
![](../../docs/images/muon-monitor.png)

View File

@ -12,8 +12,8 @@ To see Java FX demo, run `demo/spatial-showcase/Tasks/application/run` Gradle ta
##### Example view for JS:
![](../../docs/resources/spatial-showcase.png)
![](../../docs/images/spatial-showcase.png)
##### Example view for Java FX:
![](../../docs/resources/spatial-showcase-FX.png)
![](../../docs/images/spatial-showcase-FX.png)

View File

@ -5,6 +5,7 @@ import hep.dataforge.names.plus
import hep.dataforge.names.startsWith
import hep.dataforge.values.asValue
import hep.dataforge.vision.getProperty
import hep.dataforge.vision.onPropertyChange
import hep.dataforge.vision.set
import hep.dataforge.vision.setProperty
import hep.dataforge.vision.solid.*

View File

After

Width:  |  Height:  |  Size: 16 KiB

View File

After

Width:  |  Height:  |  Size: 20 KiB

View File

After

Width:  |  Height:  |  Size: 97 KiB

View File

After

Width:  |  Height:  |  Size: 87 KiB

View File

After

Width:  |  Height:  |  Size: 28 KiB

View File

After

Width:  |  Height:  |  Size: 47 KiB

View File

@ -5,14 +5,13 @@ import hep.dataforge.names.Name
import hep.dataforge.names.NameToken
import hep.dataforge.names.asName
import hep.dataforge.names.plus
import kotlinx.coroutines.launch
/**
* A container for styles
*/
public inline class StyleSheet(private val owner: VisionGroup) {
private val styleNode get() = owner.getOwnProperty(STYLESHEET_KEY).node
private val styleNode get() = owner.ownProperties[STYLESHEET_KEY].node
public val items: Map<NameToken, Meta>? get() = styleNode?.items?.mapValues { it.value.node ?: Meta.EMPTY }
@ -55,9 +54,7 @@ internal fun Vision.styleChanged(key: String, oldStyle: Meta?, newStyle: Meta?)
val tokens: Collection<Name> =
((oldStyle?.items?.keys ?: emptySet()) + (newStyle?.items?.keys ?: emptySet()))
.map { it.asName() }
parent?.scope?.launch {
tokens.forEach { parent?.notifyPropertyChanged(it) }
}
tokens.forEach { parent?.invalidateProperty(it) }
}
if (this is VisionGroup) {
for (obj in this) {
@ -71,7 +68,7 @@ internal fun Vision.styleChanged(key: String, oldStyle: Meta?, newStyle: Meta?)
* List of names of styles applied to this object. Order matters. Not inherited.
*/
public var Vision.styles: List<String>
get() = getOwnProperty(Vision.STYLE_KEY)?.stringList ?: emptyList()
get() = ownProperties[Vision.STYLE_KEY]?.stringList ?: emptyList()
set(value) {
setProperty(Vision.STYLE_KEY, value)
}
@ -86,7 +83,7 @@ public val VisionGroup.styleSheet: StyleSheet get() = StyleSheet(this)
* Add style name to the list of styles to be resolved later. The style with given name does not necessary exist at the moment.
*/
public fun Vision.useStyle(name: String) {
styles = (getOwnProperty(Vision.STYLE_KEY)?.stringList ?: emptyList()) + name
styles = (ownProperties[Vision.STYLE_KEY]?.stringList ?: emptyList()) + name
}
@ -94,7 +91,7 @@ public fun Vision.useStyle(name: String) {
* Find a style with given name for given [Vision]. The style is not necessary applied to this [Vision].
*/
public tailrec fun Vision.getStyle(name: String): Meta? =
getOwnProperty(StyleSheet.STYLESHEET_KEY + name).node ?: parent?.getStyle(name)
ownProperties[StyleSheet.STYLESHEET_KEY + name].node ?: parent?.getStyle(name)
/**
* Resolve an item in all style layers

View File

@ -1,9 +1,6 @@
package hep.dataforge.vision
import hep.dataforge.meta.DFExperimental
import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaItem
import hep.dataforge.meta.MutableItemProvider
import hep.dataforge.meta.*
import hep.dataforge.meta.descriptors.Described
import hep.dataforge.meta.descriptors.NodeDescriptor
import hep.dataforge.meta.descriptors.get
@ -12,11 +9,10 @@ import hep.dataforge.names.Name
import hep.dataforge.names.asName
import hep.dataforge.names.toName
import hep.dataforge.vision.Vision.Companion.TYPE
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.serialization.Transient
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
/**
* A root type for display hierarchy
@ -27,26 +23,8 @@ public interface Vision : Described {
/**
* The parent object of this one. If null, this one is a root.
*/
@Transient
public var parent: VisionGroup?
/**
* Properties belonging to this [Vision] potentially including artificial properties
*/
@Transient
public val meta: Meta
/**
* A coroutine scope for asynchronous calls and locks
*/
public val scope: CoroutineScope get() = parent?.scope ?: GlobalScope
/**
* A fast accessor method to get own property (no inheritance or styles).
* Should be equivalent to `getProperty(name,false,false,false)`.
*/
public fun getOwnProperty(name: Name): MetaItem?
/**
* Get property.
* @param inherit toggles parent node property lookup. Null means inference from descriptor. Default is false.
@ -59,38 +37,32 @@ public interface Vision : Described {
includeDefaults: Boolean = true,
): MetaItem?
/**
* Get an intrinsic property of this Vision excluding any inheritance or defaults. In most cases should be the same as
* `getProperty(name, false, false, false`.
*/
public fun getOwnProperty(name: Name): MetaItem? = getProperty(
name,
inherit = false,
includeStyles = false,
includeDefaults = false
)
/**
* Set the property value
*/
public fun setProperty(name: Name, item: MetaItem?, notify: Boolean = true)
/**
* Subscribe on property updates. The subscription is bound to the given [scope] and canceled when the scope is canceled
*/
public fun onPropertyChange(scope: CoroutineScope, callback: suspend (Name) -> Unit)
/**
* Flow of property invalidation events. It does not contain property values after invalidation since it is not clear
* if it should include inherited properties etc.
*/
@DFExperimental
@OptIn(ExperimentalCoroutinesApi::class)
public val propertyChanges: Flow<Name>
get() = callbackFlow<Name> {
coroutineScope {
onPropertyChange(this) {
send(it)
}
awaitClose { cancel() }
}
}
/**
* Notify all listeners that a property has been changed and should be invalidated
*/
public suspend fun notifyPropertyChanged(propertyName: Name): Unit
public fun invalidateProperty(propertyName: Name): Unit
/**
* Update this vision using a dif represented by [VisionChange].
@ -107,12 +79,19 @@ public interface Vision : Described {
}
}
public fun Vision.asyncNotifyPropertyChange(propertyName: Name) {
scope.launch {
notifyPropertyChanged(propertyName)
}
/**
* Root property node
*/
public val Vision.meta: Meta get() = ownProperties[Name.EMPTY]?.node ?: Meta.EMPTY
/**
* Subscribe on property updates. The subscription is bound to the given [scope] and canceled when the scope is canceled
*/
public fun Vision.onPropertyChange(scope: CoroutineScope, callback: suspend (Name) -> Unit) {
propertyChanges.onEach(callback).launchIn(scope)
}
/**
* Own properties, excluding inheritance, styles and descriptor
*/
@ -122,7 +101,6 @@ public val Vision.ownProperties: MutableItemProvider
override fun setItem(name: Name, item: MetaItem?): Unit = setProperty(name, item)
}
/**
* Convenient accessor for all properties of a vision.
* @param inherit - inherit property value from the parent by default. If null, inheritance is inferred from descriptor

View File

@ -11,10 +11,9 @@ import hep.dataforge.values.Null
import hep.dataforge.values.ValueType
import hep.dataforge.vision.Vision.Companion.STYLE_KEY
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@ -32,13 +31,14 @@ internal data class PropertyListener(
*/
@Serializable
@SerialName("vision")
public open class VisionBase(internal var properties: Config? = null) : Vision {
public open class VisionBase(
internal var properties: Config? = null,
@Transient public val coroutineScope: CoroutineScope = GlobalScope,
) : Vision {
@Transient
override var parent: VisionGroup? = null
override val meta: Meta get() = properties ?: Meta.EMPTY
@Synchronized
protected fun getOrCreateConfig(): Config {
if (properties == null) {
@ -51,8 +51,10 @@ public open class VisionBase(internal var properties: Config? = null) : Vision {
/**
* A fast accessor method to get own property (no inheritance or styles
*/
override fun getOwnProperty(name: Name): MetaItem? {
return properties?.getItem(name)
override fun getOwnProperty(name: Name): MetaItem? = if (name == Name.EMPTY) {
properties?.asMetaItem()
} else {
properties?.getItem(name)
}
override fun getProperty(
@ -60,7 +62,10 @@ public open class VisionBase(internal var properties: Config? = null) : Vision {
inherit: Boolean,
includeStyles: Boolean,
includeDefaults: Boolean,
): MetaItem? = sequence {
): MetaItem? = if (!inherit && !includeStyles && !includeDefaults) {
getOwnProperty(name)
} else {
sequence {
yield(getOwnProperty(name))
if (includeStyles) {
yieldAll(getStyleItems(name))
@ -72,13 +77,12 @@ public open class VisionBase(internal var properties: Config? = null) : Vision {
yield(descriptor?.get(name)?.defaultItem())
}
}.merge()
}
override fun setProperty(name: Name, item: MetaItem?, notify: Boolean) {
getOrCreateConfig().setItem(name, item)
if (notify) {
scope.launch {
notifyPropertyChanged(name)
}
invalidateProperty(name)
}
}
@ -89,7 +93,7 @@ public open class VisionBase(internal var properties: Config? = null) : Vision {
.flatMap { it.items.asSequence() }
.distinctBy { it.key }
.forEach {
notifyPropertyChanged(it.key.asName())
invalidateProperty(it.key.asName())
}
}
@ -99,18 +103,17 @@ public open class VisionBase(internal var properties: Config? = null) : Vision {
private val propertyInvalidationFlow: MutableSharedFlow<Name> = MutableSharedFlow()
@DFExperimental
override val propertyChanges: Flow<Name> get() = propertyInvalidationFlow
override val propertyChanges: Flow<Name>
get() = propertyInvalidationFlow
override fun onPropertyChange(scope: CoroutineScope, callback: suspend (Name) -> Unit) {
propertyInvalidationFlow.onEach(callback).launchIn(scope)
}
override suspend fun notifyPropertyChanged(propertyName: Name) {
override fun invalidateProperty(propertyName: Name) {
coroutineScope.launch {
if (propertyName == STYLE_KEY) {
updateStyles(styles)
}
propertyInvalidationFlow.emit(propertyName)
}
}
override fun update(change: VisionChange) {
change.properties?.let {

View File

@ -82,7 +82,7 @@ private fun CoroutineScope.collectChange(
//Collect properties change
source.onPropertyChange(this) { propertyName ->
val newItem = source.getOwnProperty(propertyName)
val newItem = source.ownProperties[propertyName]
collector().propertyChanged(name, propertyName, newItem)
}

View File

@ -31,10 +31,10 @@ public open class VisionGroupBase(
}
}
override suspend fun notifyPropertyChanged(propertyName: Name) {
super.notifyPropertyChanged(propertyName)
override fun invalidateProperty(propertyName: Name) {
super.invalidateProperty(propertyName)
for (obj in this) {
obj.notifyPropertyChanged(propertyName)
obj.invalidateProperty(propertyName)
}
}
@ -47,7 +47,7 @@ public open class VisionGroupBase(
* Propagate children change event upwards
*/
private fun childrenChanged(name: NameToken, before: Vision?, after: Vision?) {
scope.launch {
coroutineScope.launch {
_structureChanges.emit(MutableVisionGroup.StructureChange(name, before, after))
}
}

View File

@ -4,6 +4,7 @@ import hep.dataforge.meta.DFExperimental
import hep.dataforge.meta.set
import hep.dataforge.vision.VisionBase
import hep.dataforge.vision.configure
import hep.dataforge.vision.meta
import kotlinx.html.*
import kotlinx.html.stream.createHTML
import kotlin.test.Test

View File

@ -2,6 +2,7 @@ package hep.dataforge.vision.solid
import hep.dataforge.names.*
import hep.dataforge.vision.Vision
import hep.dataforge.vision.onPropertyChange
import javafx.scene.Group
import javafx.scene.Node
import kotlin.reflect.KClass

View File

@ -4,15 +4,18 @@ import hep.dataforge.meta.*
import hep.dataforge.names.Name
import hep.dataforge.names.startsWith
import hep.dataforge.names.toName
import hep.dataforge.values.Value
import hep.dataforge.vision.Vision
import hep.dataforge.vision.onPropertyChange
import javafx.application.Platform
import javafx.beans.binding.Binding
import javafx.beans.binding.ObjectBinding
import tornadofx.*
/**
* A caching binding collection for [Vision] properties
*/
class VisualObjectFXBinding(val fx: FX3DPlugin, val obj: Vision) {
public class VisualObjectFXBinding(public val fx: FX3DPlugin, public val obj: Vision) {
private val bindings = HashMap<Name, ObjectBinding<MetaItem?>>()
init {
@ -31,7 +34,7 @@ class VisualObjectFXBinding(val fx: FX3DPlugin, val obj: Vision) {
}
}
operator fun get(key: Name): ObjectBinding<MetaItem?> {
public operator fun get(key: Name): ObjectBinding<MetaItem?> {
return bindings.getOrPut(key) {
object : ObjectBinding<MetaItem?>() {
override fun computeValue(): MetaItem? = obj.getProperty(key)
@ -39,10 +42,10 @@ class VisualObjectFXBinding(val fx: FX3DPlugin, val obj: Vision) {
}
}
operator fun get(key: String) = get(key.toName())
public operator fun get(key: String) = get(key.toName())
}
fun ObjectBinding<MetaItem?>.value() = objectBinding { it.value }
public fun ObjectBinding<MetaItem?>.value(): Binding<Value?> = objectBinding { it.value }
fun ObjectBinding<MetaItem?>.string() = stringBinding { it.string }
fun ObjectBinding<MetaItem?>.number() = objectBinding { it.number }
fun ObjectBinding<MetaItem?>.double() = objectBinding { it.double }

View File

@ -3,6 +3,7 @@ package hep.dataforge.vision.gdml
import hep.dataforge.meta.DFExperimental
import hep.dataforge.meta.itemSequence
import hep.dataforge.vision.Vision
import hep.dataforge.vision.meta
import hep.dataforge.vision.solid.*
public expect class Counter() {

View File

@ -152,21 +152,21 @@ public var Solid.x: Number
get() = position?.x ?: 0f
set(value) {
position().x = value.toDouble()
asyncNotifyPropertyChange(Solid.X_POSITION_KEY)
invalidateProperty(Solid.X_POSITION_KEY)
}
public var Solid.y: Number
get() = position?.y ?: 0f
set(value) {
position().y = value.toDouble()
asyncNotifyPropertyChange(Solid.Y_POSITION_KEY)
invalidateProperty(Solid.Y_POSITION_KEY)
}
public var Solid.z: Number
get() = position?.z ?: 0f
set(value) {
position().z = value.toDouble()
asyncNotifyPropertyChange(Solid.Z_POSITION_KEY)
invalidateProperty(Solid.Z_POSITION_KEY)
}
private fun Solid.rotation(): Point3D =
@ -176,21 +176,21 @@ public var Solid.rotationX: Number
get() = rotation?.x ?: 0f
set(value) {
rotation().x = value.toDouble()
asyncNotifyPropertyChange(Solid.X_ROTATION_KEY)
invalidateProperty(Solid.X_ROTATION_KEY)
}
public var Solid.rotationY: Number
get() = rotation?.y ?: 0f
set(value) {
rotation().y = value.toDouble()
asyncNotifyPropertyChange(Solid.Y_ROTATION_KEY)
invalidateProperty(Solid.Y_ROTATION_KEY)
}
public var Solid.rotationZ: Number
get() = rotation?.z ?: 0f
set(value) {
rotation().z = value.toDouble()
asyncNotifyPropertyChange(Solid.Z_ROTATION_KEY)
invalidateProperty(Solid.Z_ROTATION_KEY)
}
private fun Solid.scale(): Point3D =
@ -200,19 +200,19 @@ public var Solid.scaleX: Number
get() = scale?.x ?: 1f
set(value) {
scale().x = value.toDouble()
asyncNotifyPropertyChange(Solid.X_SCALE_KEY)
invalidateProperty(Solid.X_SCALE_KEY)
}
public var Solid.scaleY: Number
get() = scale?.y ?: 1f
set(value) {
scale().y = value.toDouble()
asyncNotifyPropertyChange(Solid.Y_SCALE_KEY)
invalidateProperty(Solid.Y_SCALE_KEY)
}
public var Solid.scaleZ: Number
get() = scale?.z ?: 1f
set(value) {
scale().z = value.toDouble()
asyncNotifyPropertyChange(Solid.Z_SCALE_KEY)
invalidateProperty(Solid.Z_SCALE_KEY)
}

View File

@ -107,8 +107,6 @@ internal class Prototypes(
}
}
override fun getOwnProperty(name: Name): MetaItem? = null
override fun getProperty(
name: Name,
inherit: Boolean,

View File

@ -3,9 +3,9 @@ package hep.dataforge.vision.solid
import hep.dataforge.meta.*
import hep.dataforge.meta.descriptors.NodeDescriptor
import hep.dataforge.names.*
import hep.dataforge.values.Null
import hep.dataforge.vision.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@ -18,8 +18,7 @@ private fun SolidReference.getRefProperty(
inherit: Boolean,
includeStyles: Boolean,
includeDefaults: Boolean,
): MetaItem? {
return sequence {
): MetaItem? = sequence {
yield(getOwnProperty(name))
if (includeStyles) {
yieldAll(getStyleItems(name))
@ -28,8 +27,7 @@ private fun SolidReference.getRefProperty(
if (inherit) {
yield(parent?.getProperty(name, inherit))
}
}.merge()
}
}.merge()
/**
* A reference [Solid] to reuse a template object
@ -100,8 +98,6 @@ public class SolidReferenceGroup(
ReferenceChild(childName + key.asName())
} ?: emptyMap()
override val meta: Meta get() = TODO()// getChildProperty(childName, Name.EMPTY).node ?: Meta.EMPTY
override fun getOwnProperty(name: Name): MetaItem? = getChildProperty(childName, name)
override fun setProperty(name: Name, item: MetaItem?, notify: Boolean) {
@ -113,7 +109,11 @@ public class SolidReferenceGroup(
inherit: Boolean,
includeStyles: Boolean,
includeDefaults: Boolean,
): MetaItem? = getRefProperty(name, inherit, includeStyles, includeDefaults)
): MetaItem? = if (!inherit && !includeStyles && !includeDefaults) {
getOwnProperty(name)
} else {
getRefProperty(name, inherit, includeStyles, includeDefaults)
}
override var parent: VisionGroup?
get() {
@ -124,16 +124,18 @@ public class SolidReferenceGroup(
error("Setting a parent for a reference child is not possible")
}
override fun onPropertyChange(scope: CoroutineScope, callback: suspend (Name) -> Unit) {
this@SolidReferenceGroup.onPropertyChange(scope) { name ->
@DFExperimental
override val propertyChanges: Flow<Name>
get() = this@SolidReferenceGroup.propertyChanges.mapNotNull { name ->
if (name.startsWith(childToken(childName))) {
callback(name.cutFirst())
}
name.cutFirst()
} else {
null
}
}
override suspend fun notifyPropertyChanged(propertyName: Name) {
this@SolidReferenceGroup.notifyPropertyChanged(childPropertyName(childName, propertyName))
override fun invalidateProperty(propertyName: Name) {
this@SolidReferenceGroup.invalidateProperty(childPropertyName(childName, propertyName))
}
override fun update(change: VisionChange) {

View File

@ -4,6 +4,7 @@ import hep.dataforge.names.Name
import hep.dataforge.names.toName
import hep.dataforge.vision.MutableVisionGroup
import hep.dataforge.vision.get
import hep.dataforge.vision.meta
import kotlin.test.Test
import kotlin.test.assertEquals

View File

@ -2,19 +2,18 @@ package hep.dataforge.vision.solid
import hep.dataforge.vision.get
import hep.dataforge.vision.style
import hep.dataforge.vision.styles
import hep.dataforge.vision.useStyle
import kotlin.test.Test
import kotlin.test.assertEquals
class SolidReferenceTest {
val groupWithReference = SolidGroup {
val referenceStyle by style {
val theStyle by style {
SolidMaterial.MATERIAL_COLOR_KEY put "red"
}
ref("test", Box(100f,100f,100f).apply {
color("blue")
useStyle(referenceStyle)
useStyle(theStyle)
})
}

View File

@ -6,6 +6,7 @@ import hep.dataforge.names.Name
import hep.dataforge.names.asName
import hep.dataforge.names.plus
import hep.dataforge.names.startsWith
import hep.dataforge.vision.onPropertyChange
import hep.dataforge.vision.solid.Solid
import hep.dataforge.vision.solid.SolidMaterial
import hep.dataforge.vision.solid.layer

View File

@ -2,6 +2,7 @@ package hep.dataforge.vision.solid.three
import hep.dataforge.context.logger
import hep.dataforge.vision.onPropertyChange
import hep.dataforge.vision.solid.SolidLabel
import info.laht.threekt.core.Object3D
import info.laht.threekt.geometries.TextBufferGeometry

View File

@ -1,6 +1,7 @@
package hep.dataforge.vision.solid.three
import hep.dataforge.meta.node
import hep.dataforge.vision.onPropertyChange
import hep.dataforge.vision.solid.PolyLine
import hep.dataforge.vision.solid.color
import hep.dataforge.vision.solid.string

View File

@ -7,6 +7,7 @@ import hep.dataforge.values.int
import hep.dataforge.values.string
import hep.dataforge.vision.Colors
import hep.dataforge.vision.Vision
import hep.dataforge.vision.ownProperties
import hep.dataforge.vision.solid.SolidMaterial
import info.laht.threekt.materials.LineBasicMaterial
import info.laht.threekt.materials.Material
@ -119,7 +120,7 @@ private var Material.cached: Boolean
public fun Mesh.updateMaterial(vision: Vision) {
//val meta = vision.getProperty(SolidMaterial.MATERIAL_KEY, inherit = true).node
val ownMaterialMeta = vision.getOwnProperty(SolidMaterial.MATERIAL_KEY)
val ownMaterialMeta = vision.ownProperties[SolidMaterial.MATERIAL_KEY]
val parentMaterialMeta = vision.parent?.getProperty(
SolidMaterial.MATERIAL_KEY,
inherit = true,

View File

@ -6,6 +6,7 @@ import hep.dataforge.names.*
import hep.dataforge.vision.Vision
import hep.dataforge.vision.VisionForge
import hep.dataforge.vision.client.ElementVisionRenderer
import hep.dataforge.vision.onPropertyChange
import hep.dataforge.vision.solid.*
import hep.dataforge.vision.solid.specifications.Canvas3DOptions
import hep.dataforge.vision.visible

View File

@ -3,6 +3,7 @@ package hep.dataforge.vision.solid.three
import hep.dataforge.names.cutFirst
import hep.dataforge.names.firstOrNull
import hep.dataforge.names.toName
import hep.dataforge.vision.onPropertyChange
import hep.dataforge.vision.solid.Solid
import hep.dataforge.vision.solid.SolidReferenceGroup
import hep.dataforge.vision.solid.SolidReferenceGroup.Companion.REFERENCE_CHILD_PROPERTY_PREFIX