From b2ba92e745c10f9b1764fbf67f81ca042bf47140 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 15 Dec 2020 19:18:04 +0300 Subject: [PATCH 01/19] Change property and structure subscription to flows. --- CHANGELOG.md | 3 +- .../kotlin/ru/mipt/npm/muon/monitor/Model.kt | 2 - .../kotlin/hep/dataforge/vision/StyleSheet.kt | 14 +- .../kotlin/hep/dataforge/vision/Vision.kt | 95 +++++---- .../kotlin/hep/dataforge/vision/VisionBase.kt | 131 ++++++------ .../hep/dataforge/vision/VisionChange.kt | 94 ++++----- .../hep/dataforge/vision/VisionGroup.kt | 14 +- .../hep/dataforge/vision/VisionGroupBase.kt | 99 ++++----- .../kotlin/hep/dataforge/vision/misc.kt | 22 +- .../hep/dataforge/vision/html/HtmlTagTest.kt | 3 +- .../editor/VisualObjectEditorFragment.kt | 12 +- .../hep/dataforge/vision/solid/FX3DPlugin.kt | 2 +- .../vision/solid/FXReferenceFactory.kt | 8 +- .../dataforge/vision/gdml/GDMLTransformer.kt | 10 +- .../dataforge/vision/gdml/GdmlOptimizer.kt | 19 +- .../hep/dataforge/vision/solid/Composite.kt | 14 +- .../hep/dataforge/vision/solid/Solid.kt | 33 ++- .../hep/dataforge/vision/solid/SolidGroup.kt | 33 ++- .../dataforge/vision/solid/SolidManager.kt | 2 +- .../dataforge/vision/solid/SolidMaterial.kt | 11 +- .../dataforge/vision/solid/SolidReference.kt | 156 --------------- .../vision/solid/SolidReferenceGroup.kt | 189 ++++++++++++++++++ .../solid/transform/RemoveSingleChild.kt | 9 +- .../dataforge/vision/solid/transform/UnRef.kt | 8 +- .../dataforge/vision/solid/PropertyTest.kt | 6 +- .../vision/solid/SerializationTest.kt | 7 +- .../vision/solid/VisionUpdateTest.kt | 2 +- .../vision/solid/three/MeshThreeFactory.kt | 22 +- .../vision/solid/three/ThreeConvexFactory.kt | 3 +- .../vision/solid/three/ThreeFactory.kt | 2 +- .../vision/solid/three/ThreeLabelFactory.kt | 12 +- .../vision/solid/three/ThreeLineFactory.kt | 8 +- .../vision/solid/three/ThreePlugin.kt | 16 +- .../solid/three/ThreeReferenceFactory.kt | 19 +- 34 files changed, 562 insertions(+), 518 deletions(-) delete mode 100644 visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidReference.kt create mode 100644 visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidReferenceGroup.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index 611e4c13..42282a71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,13 +4,14 @@ ### Added - Server module - Change collector +- Customizable accessors for colors ### Changed - Vision does not implement ItemProvider anymore. Property changes are done via `getProperty`/`setProperty` and `property` delegate. - Point3D and Point2D are made separate classes instead of expect/actual (to split up different engines. - JavaFX support moved to a separate module - Threejs support moved to a separate module -- \[Breaking change!\] Stylesheets are moved into properties under `@stylesheet` key +- \[Format breaking change!\] Stylesheets are moved into properties under `@stylesheet` key ### Deprecated diff --git a/demo/muon-monitor/src/commonMain/kotlin/ru/mipt/npm/muon/monitor/Model.kt b/demo/muon-monitor/src/commonMain/kotlin/ru/mipt/npm/muon/monitor/Model.kt index 73af1875..9027b26a 100644 --- a/demo/muon-monitor/src/commonMain/kotlin/ru/mipt/npm/muon/monitor/Model.kt +++ b/demo/muon-monitor/src/commonMain/kotlin/ru/mipt/npm/muon/monitor/Model.kt @@ -1,7 +1,6 @@ package ru.mipt.npm.muon.monitor import hep.dataforge.vision.removeAll -import hep.dataforge.vision.setProperty import hep.dataforge.vision.solid.* import ru.mipt.npm.muon.monitor.Monitor.CENTRAL_LAYER_Z import ru.mipt.npm.muon.monitor.Monitor.LOWER_LAYER_Z @@ -63,7 +62,6 @@ class Model { fun reset() { map.values.forEach { - it.config it.setProperty(SolidMaterial.MATERIAL_COLOR_KEY, null) } tracks.removeAll() diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/StyleSheet.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/StyleSheet.kt index 4d3ea1f3..e7a6d971 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/StyleSheet.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/StyleSheet.kt @@ -11,7 +11,7 @@ import hep.dataforge.names.plus */ public inline class StyleSheet(private val owner: VisionGroup) { - private val styleNode get() = owner.properties?.get(STYLESHEET_KEY).node + private val styleNode get() = owner.getOwnProperty(STYLESHEET_KEY).node public val items: Map? get() = styleNode?.items?.mapValues { it.value.node ?: Meta.EMPTY } @@ -21,11 +21,7 @@ public inline class StyleSheet(private val owner: VisionGroup) { * Define a style without notifying owner */ public fun define(key: String, style: Meta?) { - if (style == null) { - styleNode?.remove(key) - } else { - owner.config[STYLESHEET_KEY + key] = style - } + owner.setProperty(STYLESHEET_KEY + key, style) } /** @@ -58,7 +54,7 @@ internal fun Vision.styleChanged(key: String, oldStyle: Meta?, newStyle: Meta?) val tokens: Collection = ((oldStyle?.items?.keys ?: emptySet()) + (newStyle?.items?.keys ?: emptySet())) .map { it.asName() } - tokens.forEach { parent?.propertyChanged(it) } + tokens.forEach { parent?.notifyPropertyChanged(it) } } if (this is VisionGroup) { for (obj in this) { @@ -78,7 +74,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 = (properties[Vision.STYLE_KEY]?.stringList ?: emptyList()) + name + styles = (getOwnProperty(Vision.STYLE_KEY)?.stringList ?: emptyList()) + name } @@ -86,7 +82,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? = - properties?.get(StyleSheet.STYLESHEET_KEY + name).node ?: parent?.getStyle(name) + getOwnProperty(StyleSheet.STYLESHEET_KEY + name).node ?: parent?.getStyle(name) /** * Resolve an item in all style layers diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt index 058a04b3..4baa5b8a 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt @@ -10,13 +10,14 @@ import hep.dataforge.provider.Type import hep.dataforge.values.asValue import hep.dataforge.vision.Vision.Companion.TYPE import hep.dataforge.vision.Vision.Companion.VISIBLE_KEY +import kotlinx.coroutines.flow.Flow import kotlinx.serialization.Transient /** * A root type for display hierarchy */ @Type(TYPE) -public interface Vision : Configurable, Described { +public interface Vision : Described { /** * The parent object of this one. If null, this one is a root. @@ -25,42 +26,52 @@ public interface Vision : Configurable, Described { public var parent: VisionGroup? /** - * Nullable version of [config] used to check if this [Vision] has custom properties + * A fast accessor method to get own property (no inheritance or styles */ - public val properties: Config? + public fun getOwnProperty(name: Name): MetaItem<*>? /** - * All properties including styles and prototypes if present, including inherited ones + * Get property. + * @param inherit toggles parent node property lookup + * @param includeStyles toggles inclusion of */ - public val allProperties: Laminate + public fun getProperty( + name: Name, + inherit: Boolean = true, + includeStyles: Boolean = true, + includeDefaults: Boolean = true, + ): MetaItem<*>? + /** - * Get property (including styles). [inherit] toggles parent node property lookup + * Set the property value */ - public fun getProperty(name: Name, inherit: Boolean = true): MetaItem<*>? + public fun setProperty(name: Name, item: MetaItem<*>?) /** - * Trigger property invalidation event. If [name] is empty, notify that the whole object is changed + * 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. */ - public fun propertyChanged(name: Name): Unit + public val propertyInvalidated: Flow + /** - * Add listener triggering on property change + * Notify all listeners that a property has been changed and should be invalidated */ - public fun onPropertyChange(owner: Any?, action: (Name) -> Unit): Unit - - /** - * Remove change listeners with given owner. - */ - public fun removeChangeListener(owner: Any?) + public fun notifyPropertyChanged(propertyName: Name): Unit /** * List of names of styles applied to this object. Order matters. Not inherited. */ public var styles: List - get() = properties[STYLE_KEY]?.stringList ?: emptyList() + get() = getProperty( + STYLE_KEY, + inherit = false, + includeStyles = false, + includeDefaults = true + )?.stringList ?: emptyList() set(value) { - config[STYLE_KEY] = value + setProperty(STYLE_KEY, value) } /** @@ -78,24 +89,42 @@ public interface Vision : Configurable, Described { } } +/** + * Convenient accessor for all properties of a vision. Provided properties include styles and defaults, but do not inherit. + */ +public val Vision.properties: MutableItemProvider + get() = object : MutableItemProvider { + override fun getItem(name: Name): MetaItem<*>? = getProperty(name, + inherit = false, + includeStyles = true, + includeDefaults = true + ) + + override fun setItem(name: Name, item: MetaItem<*>?): Unit = setProperty(name, item) + } + /** * Get [Vision] property using key as a String */ -public fun Vision.getProperty(key: String, inherit: Boolean = true): MetaItem<*>? = - getProperty(key.toName(), inherit) +public fun Vision.getProperty( + key: String, + inherit: Boolean = true, + includeStyles: Boolean = true, + includeDefaults: Boolean = true, +): MetaItem<*>? = getProperty(key.toName(), inherit, includeStyles, includeDefaults) /** * A convenience method to pair [getProperty] */ public fun Vision.setProperty(key: Name, value: Any?) { - config[key] = value + properties[key] = value } /** * A convenience method to pair [getProperty] */ public fun Vision.setProperty(key: String, value: Any?) { - config[key] = value + properties[key] = value } /** @@ -103,27 +132,7 @@ public fun Vision.setProperty(key: String, value: Any?) { */ public var Vision.visible: Boolean? get() = getProperty(VISIBLE_KEY).boolean - set(value) = config.setValue(VISIBLE_KEY, value?.asValue()) - -///** -// * Convenience delegate for properties -// */ -//public fun Vision.property( -// default: MetaItem<*>? = null, -// key: Name? = null, -// inherit: Boolean = true, -//): MutableItemDelegate = -// object : ReadWriteProperty?> { -// override fun getValue(thisRef: Any?, property: KProperty<*>): MetaItem<*>? { -// val name = key ?: property.name.toName() -// return getProperty(name, inherit) ?: default -// } -// -// override fun setValue(thisRef: Any?, property: KProperty<*>, value: MetaItem<*>?) { -// val name = key ?: property.name.toName() -// setProperty(name, value) -// } -// } + set(value) = setProperty(VISIBLE_KEY, value?.asValue()) public fun Vision.props(inherit: Boolean = true): MutableItemProvider = object : MutableItemProvider { override fun getItem(name: Name): MetaItem<*>? { diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt index 6127479c..19080b67 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt @@ -3,15 +3,18 @@ package hep.dataforge.vision import hep.dataforge.meta.* import hep.dataforge.meta.descriptors.NodeDescriptor import hep.dataforge.meta.descriptors.defaultItem -import hep.dataforge.meta.descriptors.defaultMeta import hep.dataforge.meta.descriptors.get import hep.dataforge.names.Name import hep.dataforge.names.asName import hep.dataforge.values.ValueType import hep.dataforge.vision.Vision.Companion.STYLE_KEY +import kotlinx.coroutines.channels.BufferOverflow +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.Transient +import kotlin.jvm.Synchronized internal data class PropertyListener( val owner: Any? = null, @@ -28,83 +31,89 @@ public open class VisionBase : Vision { /** * Object own properties excluding styles and inheritance */ - override var properties: Config? = null - protected set + @SerialName("properties") + private var _properties: Config? = null - override val descriptor: NodeDescriptor? get() = null + /** + * All own properties as a read-only Meta + */ + public val ownProperties: Meta get() = _properties?: Meta.EMPTY - protected fun updateStyles(names: List) { - names.mapNotNull { getStyle(it) }.asSequence() - .flatMap { it.items.asSequence() } - .distinctBy { it.key } - .forEach { - propertyChanged(it.key.asName()) + @Synchronized + private fun getOrCreateConfig(): Config { + if (_properties == null) { + val newProperties = Config() + _properties = newProperties + newProperties.onChange(this) { name, oldItem, newItem -> + if (oldItem != newItem) { + notifyPropertyChanged(name) + } } + } + return _properties!! } /** - * The config is initialized and assigned on-demand. - * To avoid unnecessary allocations, one should access [getAllProperties] via [getProperty] instead. + * A fast accessor method to get own property (no inheritance or styles */ - override val config: Config by lazy { - properties ?: Config().also { config -> - properties = config.also { - it.onChange(this) { name, _, _ -> propertyChanged(name) } - } + override fun getOwnProperty(name: Name): MetaItem<*>? { + return _properties?.getItem(name) + } + + override fun getProperty( + name: Name, + inherit: Boolean, + includeStyles: Boolean, + includeDefaults: Boolean, + ): MetaItem<*>? = sequence { + yield(getOwnProperty(name)) + if (includeStyles) { + yieldAll(getStyleItems(name)) } - } - - @Transient - private val listeners = HashSet() - - override fun propertyChanged(name: Name) { - if (name == STYLE_KEY) { - updateStyles(properties?.get(STYLE_KEY)?.stringList ?: emptyList()) - } - for (listener in listeners) { - listener.action(name) - } - } - - override fun onPropertyChange(owner: Any?, action: (Name) -> Unit) { - listeners.add(PropertyListener(owner, action)) - } - - override fun removeChangeListener(owner: Any?) { - listeners.removeAll { owner == null || it.owner == owner } - } - - /** - * All available properties in a layered form - */ - override val allProperties: Laminate - get() = Laminate( - properties, - allStyles, - parent?.allProperties, - descriptor?.defaultMeta(), - ) - - override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? = sequence { - yield(properties?.get(name)) - yieldAll(getStyleItems(name)) if (inherit) { yield(parent?.getProperty(name, inherit)) } yield(descriptor?.get(name)?.defaultItem()) }.merge() - /** - * Reset all properties to their default values - */ - public fun resetProperties() { - properties?.removeListener(this) - properties = null + @Synchronized + override fun setProperty(name: Name, item: MetaItem<*>?) { + getOrCreateConfig().setItem(name, item) + notifyPropertyChanged(name) + } + + override val descriptor: NodeDescriptor? get() = null + + private fun updateStyles(names: List) { + names.mapNotNull { getStyle(it) }.asSequence() + .flatMap { it.items.asSequence() } + .distinctBy { it.key } + .forEach { + notifyPropertyChanged(it.key.asName()) + } + } + + private val _propertyInvalidationFlow: MutableSharedFlow = MutableSharedFlow( + onBufferOverflow = BufferOverflow.DROP_OLDEST + ) + + override val propertyInvalidated: SharedFlow get() = _propertyInvalidationFlow + + override fun notifyPropertyChanged(propertyName: Name) { + if (propertyName == STYLE_KEY) { + updateStyles(properties.getItem(STYLE_KEY)?.stringList ?: emptyList()) + } + + _propertyInvalidationFlow.tryEmit(propertyName) + } + + public fun configure(block: MutableMeta<*>.() -> Unit) { + getOrCreateConfig().block() } override fun update(change: VisionChange) { - change.propertyChange[Name.EMPTY]?.let { - config.update(it) + change.properties?.let { + getOrCreateConfig().update(it) } } diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionChange.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionChange.kt index ca7396a7..f2ac5d6b 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionChange.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionChange.kt @@ -6,7 +6,10 @@ import hep.dataforge.names.plus import kotlinx.coroutines.* import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import kotlinx.serialization.* +import kotlin.jvm.Synchronized import kotlin.time.Duration /** @@ -14,29 +17,41 @@ import kotlin.time.Duration */ public class VisionChangeBuilder : VisionContainerBuilder { - private val propertyChange = HashMap() - private val childrenChange = HashMap() + private var reset: Boolean = false + private var vision: Vision? = null + private val propertyChange = Config() + private val children: HashMap = HashMap() - public fun isEmpty(): Boolean = propertyChange.isEmpty() && childrenChange.isEmpty() + public fun isEmpty(): Boolean = propertyChange.isEmpty() && propertyChange.isEmpty() && children.isEmpty() + + @Synchronized + private fun getOrPutChild(visionName: Name): VisionChangeBuilder = + children.getOrPut(visionName) { VisionChangeBuilder() } public fun propertyChanged(visionName: Name, propertyName: Name, item: MetaItem<*>?) { - propertyChange - .getOrPut(visionName) { Config() } - .setItem(propertyName, item) + if (visionName == Name.EMPTY) { + propertyChange[propertyName] = item + } else { + getOrPutChild(visionName).propertyChanged(Name.EMPTY, propertyName, item) + } } override fun set(name: Name, child: Vision?) { - childrenChange[name] = child + getOrPutChild(name).apply { + vision = child + reset = vision == null + } } /** * Isolate collected changes by creating detached copies of given visions */ public fun isolate(manager: VisionManager): VisionChange = VisionChange( - propertyChange.mapValues { it.value.seal() }, - childrenChange.mapValues { it.value?.isolate(manager) } + reset, + vision?.isolate(manager), + if (propertyChange.isEmpty()) null else propertyChange.seal(), + if (children.isEmpty()) null else children.mapValues { it.value.isolate(manager) } ) - //TODO optimize isolation for visions without parents? } private fun Vision.isolate(manager: VisionManager): Vision { @@ -46,16 +61,13 @@ private fun Vision.isolate(manager: VisionManager): Vision { } @Serializable -public data class VisionChange( - val propertyChange: Map, - val childrenChange: Map, +public class VisionChange( + public val reset: Boolean = false, + public val vision: Vision? = null, + public val properties: Meta? = null, + public val children: Map? = null, ) { - public fun isEmpty(): Boolean = propertyChange.isEmpty() && childrenChange.isEmpty() - /** - * A shortcut to the top level property dif - */ - public val properties: Meta? get() = propertyChange[Name.EMPTY] } public inline fun VisionChange(manager: VisionManager, block: VisionChangeBuilder.() -> Unit): VisionChange = @@ -69,17 +81,10 @@ private fun CoroutineScope.collectChange( ) { //Collect properties change - source.config.onChange(this) { propertyName, oldItem, newItem -> - if (oldItem != newItem) { - launch { - collector().propertyChanged(name, propertyName, newItem) - } - } - } - - coroutineContext[Job]?.invokeOnCompletion { - source.config.removeListener(this) - } + source.propertyInvalidated.onEach { propertyName -> + val newItem = source.getProperty(propertyName, inherit = false, includeStyles = false, includeDefaults = false) + collector().propertyChanged(name, propertyName, newItem) + }.launchIn(this) if (source is VisionGroup) { //Subscribe for children changes @@ -89,17 +94,12 @@ private fun CoroutineScope.collectChange( //Subscribe for structure change if (source is MutableVisionGroup) { - source.onStructureChange(this) { token, before, after -> - before?.removeChangeListener(this) - (before as? MutableVisionGroup)?.removeStructureChangeListener(this) + source.structureChanges.onEach { (token, _, after) -> if (after != null) { collectChange(name + token, after, collector) } collector()[name + token] = after - } - coroutineContext[Job]?.invokeOnCompletion { - source.removeStructureChangeListener(this) - } + }.launchIn(this) } } } @@ -111,17 +111,19 @@ public fun Vision.flowChanges( ): Flow = flow { var collector = VisionChangeBuilder() - manager.context.collectChange(Name.EMPTY, this@flowChanges) { collector } + coroutineScope { + collectChange(Name.EMPTY, this@flowChanges) { collector } - while (currentCoroutineContext().isActive) { - //Wait for changes to accumulate - delay(collectionDuration) - //Propagate updates only if something is changed - if (!collector.isEmpty()) { - //emit changes - emit(collector.isolate(manager)) - //Reset the collector - collector = VisionChangeBuilder() + while (currentCoroutineContext().isActive) { + //Wait for changes to accumulate + delay(collectionDuration) + //Propagate updates only if something is changed + if (!collector.isEmpty()) { + //emit changes + emit(collector.isolate(manager)) + //Reset the collector + collector = VisionChangeBuilder() + } } } } \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionGroup.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionGroup.kt index fdfb9e7a..75855eb1 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionGroup.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionGroup.kt @@ -2,6 +2,7 @@ package hep.dataforge.vision import hep.dataforge.names.* import hep.dataforge.provider.Provider +import kotlinx.coroutines.flow.Flow public interface VisionContainer { public operator fun get(name: Name): V? @@ -75,19 +76,12 @@ public interface VisionContainerBuilder { */ public interface MutableVisionGroup : VisionGroup, VisionContainerBuilder { - /** - * Add listener for children structure change. - * @param owner the handler to properly remove listeners - * @param action First argument of the action is the name of changed child. Second argument is the new value of the object. - */ - public fun onStructureChange(owner: Any?, action: (token: NameToken, before: Vision?, after: Vision?) -> Unit) + public data class StructureChange(val token: NameToken, val before: Vision?, val after: Vision?) /** - * Remove children change listener + * Flow structure changes of this group. Unconsumed changes are discarded */ - public fun removeStructureChangeListener(owner: Any?) - -// public operator fun set(name: Name, child: Vision?) + public val structureChanges: Flow } public operator fun VisionContainer.get(str: String): V? = get(str.toName()) diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionGroupBase.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionGroupBase.kt index 996b0853..a1170b48 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionGroupBase.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionGroupBase.kt @@ -1,7 +1,9 @@ package hep.dataforge.vision -import hep.dataforge.meta.configure import hep.dataforge.names.* +import kotlinx.coroutines.channels.BufferOverflow +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.Transient @@ -26,54 +28,25 @@ public open class VisionGroupBase : VisionBase(), MutableVisionGroup { */ override val children: Map get() = childrenInternal - override fun propertyChanged(name: Name) { - super.propertyChanged(name) + override fun notifyPropertyChanged(propertyName: Name) { + super.notifyPropertyChanged(propertyName) for (obj in this) { - obj.propertyChanged(name) + obj.notifyPropertyChanged(propertyName) } } - private data class StructureChangeListener( - val owner: Any?, - val callback: (token: NameToken, before: Vision?, after: Vision?) -> Unit, + @Transient + private val _structureChanges: MutableSharedFlow = MutableSharedFlow( + onBufferOverflow = BufferOverflow.DROP_OLDEST ) - @Transient - private val structureChangeListeners = HashSet() - - /** - * Add listener for children change - */ - override fun onStructureChange(owner: Any?, action: (token: NameToken, before: Vision?, after: Vision?) -> Unit) { - structureChangeListeners.add( - StructureChangeListener( - owner, - action - ) - ) - } - - /** - * Remove children change listener - */ - override fun removeStructureChangeListener(owner: Any?) { - structureChangeListeners.removeAll { owner == null || it.owner === owner } - } + override val structureChanges: SharedFlow get() = _structureChanges /** * Propagate children change event upwards */ - protected fun childrenChanged(name: NameToken, before: Vision?, after: Vision?) { - structureChangeListeners.forEach { it.callback(name, before, after) } - } - - /** - * Remove a child with given name token - */ - public fun removeChild(token: NameToken): Vision? { - val removed = childrenInternal.remove(token) - removed?.parent = null - return removed + private fun childrenChanged(name: NameToken, before: Vision?, after: Vision?) { + _structureChanges.tryEmit(MutableVisionGroup.StructureChange(name, before, after)) } /** @@ -91,12 +64,22 @@ public open class VisionGroupBase : VisionBase(), MutableVisionGroup { /** * Set parent for given child and attach it */ - private fun attachChild(token: NameToken, child: Vision) { - if (child.parent == null) { - child.parent = this - childrenInternal[token] = child - } else if (child.parent !== this) { - error("Can't reassign existing parent for $child") + private fun attachChild(token: NameToken, child: Vision?) { + val before = children[token] + when { + child == null -> { + childrenInternal.remove(token) + } + child.parent == null -> { + child.parent = this + childrenInternal[token] = child + } + child.parent !== this -> { + error("Can't reassign existing parent for $child") + } + } + if (before != child) { + childrenChanged(token, before, child) } } @@ -133,13 +116,7 @@ public open class VisionGroupBase : VisionBase(), MutableVisionGroup { } name.length == 1 -> { val token = name.tokens.first() - val before = children[token] - if (child == null) { - removeChild(token) - } else { - attachChild(token, child) - } - childrenChanged(token, before, child) + attachChild(token, child) } else -> { //TODO add safety check @@ -150,18 +127,12 @@ public open class VisionGroupBase : VisionBase(), MutableVisionGroup { } override fun update(change: VisionChange) { - //update stylesheet -// val changeStyleSheet = change.styleSheet -// if (changeStyleSheet != null) { -// styleSheet { -// update(changeStyleSheet) -// } -// } - change.propertyChange.forEach {(childName,configChange)-> - get(childName)?.configure(configChange) - } - change.childrenChange.forEach { (name, child) -> - set(name, child) + change.children?.forEach { (name, change) -> + when { + change.reset -> set(name, null) + change.vision != null -> set(name, change.vision) + else -> get(name)?.update(change) + } } super.update(change) } diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/misc.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/misc.kt index 0e17c123..7c9a4521 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/misc.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/misc.kt @@ -1,9 +1,7 @@ package hep.dataforge.vision -import hep.dataforge.meta.Laminate -import hep.dataforge.meta.MetaItem +import hep.dataforge.meta.* import hep.dataforge.meta.descriptors.NodeDescriptor -import hep.dataforge.meta.node import hep.dataforge.names.Name import hep.dataforge.values.ValueType import hep.dataforge.values.asValue @@ -30,4 +28,20 @@ public inline fun > NodeDescriptor.enum(key: Name, default: default(default) } allowedValues = enumValues().map { it.asValue() } -} \ No newline at end of file +} + +@DFExperimental +public val Vision.ownProperties: Meta? + get() = (this as? VisionBase)?.ownProperties + +@DFExperimental +public val Vision.describedProperties: Meta + get() = Meta { + descriptor?.items?.forEach { (key, _) -> + key put getProperty(key) + } + } + +public fun Vision.configure(meta: Meta?): Unit = update(VisionChange(properties = meta)) + +public fun Vision.configure(block: MutableMeta<*>.() -> Unit): Unit = configure(Meta(block)) \ No newline at end of file diff --git a/visionforge-core/src/commonTest/kotlin/hep/dataforge/vision/html/HtmlTagTest.kt b/visionforge-core/src/commonTest/kotlin/hep/dataforge/vision/html/HtmlTagTest.kt index 0d40205a..0937bd64 100644 --- a/visionforge-core/src/commonTest/kotlin/hep/dataforge/vision/html/HtmlTagTest.kt +++ b/visionforge-core/src/commonTest/kotlin/hep/dataforge/vision/html/HtmlTagTest.kt @@ -1,7 +1,6 @@ package hep.dataforge.vision.html import hep.dataforge.meta.DFExperimental -import hep.dataforge.meta.configure import hep.dataforge.meta.set import hep.dataforge.vision.VisionBase import kotlinx.html.* @@ -35,7 +34,7 @@ class HtmlTagTest { div { h2 { +"Properties" } ul { - vision.properties?.items?.forEach { + (vision as? VisionBase)?.ownProperties?.items?.forEach { li { a { +it.key.toString() } p { +it.value.toString() } diff --git a/visionforge-fx/src/main/kotlin/hep/dataforge/vision/editor/VisualObjectEditorFragment.kt b/visionforge-fx/src/main/kotlin/hep/dataforge/vision/editor/VisualObjectEditorFragment.kt index 251130c2..e8b22dbe 100644 --- a/visionforge-fx/src/main/kotlin/hep/dataforge/vision/editor/VisualObjectEditorFragment.kt +++ b/visionforge-fx/src/main/kotlin/hep/dataforge/vision/editor/VisualObjectEditorFragment.kt @@ -1,12 +1,8 @@ package hep.dataforge.vision.editor -import hep.dataforge.meta.Config -import hep.dataforge.meta.Meta +import hep.dataforge.meta.* import hep.dataforge.meta.descriptors.NodeDescriptor -import hep.dataforge.meta.update -import hep.dataforge.vision.Vision -import hep.dataforge.vision.getStyle -import hep.dataforge.vision.setProperty +import hep.dataforge.vision.* import javafx.beans.binding.Binding import javafx.beans.property.SimpleObjectProperty import javafx.scene.Node @@ -23,8 +19,8 @@ class VisualObjectEditorFragment(val selector: (Vision) -> Meta) : Fragment() { constructor( item: Vision?, descriptor: NodeDescriptor?, - selector: (Vision) -> Config = { it.config } - ) : this(selector) { + selector: (Vision) -> MutableItemProvider = { it.properties } + ) : this({it.describedProperties}) { this.item = item this.descriptorProperty.set(descriptor) } diff --git a/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/FX3DPlugin.kt b/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/FX3DPlugin.kt index 3f863a34..ce88a14c 100644 --- a/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/FX3DPlugin.kt +++ b/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/FX3DPlugin.kt @@ -45,7 +45,7 @@ class FX3DPlugin : AbstractPlugin() { fun buildNode(obj: Solid): Node { val binding = VisualObjectFXBinding(obj) return when (obj) { - is SolidReference -> referenceFactory(obj, binding) + is SolidReferenceGroup -> referenceFactory(obj, binding) is SolidGroup -> { Group(obj.children.mapNotNull { (token, obj) -> (obj as? Solid)?.let { diff --git a/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/FXReferenceFactory.kt b/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/FXReferenceFactory.kt index 7c7258ac..77244de2 100644 --- a/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/FXReferenceFactory.kt +++ b/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/FXReferenceFactory.kt @@ -6,15 +6,15 @@ import javafx.scene.Group import javafx.scene.Node import kotlin.reflect.KClass -class FXReferenceFactory(val plugin: FX3DPlugin) : FX3DFactory { - override val type: KClass get() = SolidReference::class +class FXReferenceFactory(val plugin: FX3DPlugin) : FX3DFactory { + override val type: KClass get() = SolidReferenceGroup::class - override fun invoke(obj: SolidReference, binding: VisualObjectFXBinding): Node { + override fun invoke(obj: SolidReferenceGroup, binding: VisualObjectFXBinding): Node { val prototype = obj.prototype val node = plugin.buildNode(prototype) obj.onPropertyChange(this) { name-> - if (name.firstOrNull()?.body == SolidReference.REFERENCE_CHILD_PROPERTY_PREFIX) { + if (name.firstOrNull()?.body == SolidReferenceGroup.REFERENCE_CHILD_PROPERTY_PREFIX) { val childName = name.firstOrNull()?.index?.toName() ?: error("Wrong syntax for reference child property: '$name'") val propertyName = name.cutFirst() val referenceChild = obj[childName] ?: error("Reference child with name '$childName' not found") diff --git a/visionforge-gdml/src/commonMain/kotlin/hep/dataforge/vision/gdml/GDMLTransformer.kt b/visionforge-gdml/src/commonMain/kotlin/hep/dataforge/vision/gdml/GDMLTransformer.kt index d3dd5b3a..58ec3b51 100644 --- a/visionforge-gdml/src/commonMain/kotlin/hep/dataforge/vision/gdml/GDMLTransformer.kt +++ b/visionforge-gdml/src/commonMain/kotlin/hep/dataforge/vision/gdml/GDMLTransformer.kt @@ -2,12 +2,12 @@ package hep.dataforge.vision.gdml import hep.dataforge.meta.Meta import hep.dataforge.meta.MetaBuilder -import hep.dataforge.meta.set import hep.dataforge.names.Name import hep.dataforge.names.asName import hep.dataforge.names.plus import hep.dataforge.names.toName import hep.dataforge.vision.set +import hep.dataforge.vision.setProperty import hep.dataforge.vision.solid.* import hep.dataforge.vision.solid.SolidMaterial.Companion.MATERIAL_COLOR_KEY import hep.dataforge.vision.styleSheet @@ -51,13 +51,13 @@ private class GDMLTransformer(val settings: GDMLTransformerSettings) { private val proto = SolidGroup() private val solids = proto.group(solidsName) { - config["edges.enabled"] = false + setProperty("edges.enabled", false) } - private val referenceStore = HashMap>() + private val referenceStore = HashMap>() - private fun proxySolid(root: GDML, group: SolidGroup, solid: GDMLSolid, name: String): SolidReference { + private fun proxySolid(root: GDML, group: SolidGroup, solid: GDMLSolid, name: String): SolidReferenceGroup { val templateName = solidsName + name if (proto[templateName] == null) { solids.addSolid(root, solid, name) @@ -67,7 +67,7 @@ private class GDMLTransformer(val settings: GDMLTransformerSettings) { return ref } - private fun proxyVolume(root: GDML, group: SolidGroup, physVolume: GDMLPhysVolume, volume: GDMLGroup): SolidReference { + private fun proxyVolume(root: GDML, group: SolidGroup, physVolume: GDMLPhysVolume, volume: GDMLGroup): SolidReferenceGroup { val templateName = volumesName + volume.name.asName() if (proto[templateName] == null) { proto[templateName] = volume(root, volume) diff --git a/visionforge-gdml/src/commonMain/kotlin/hep/dataforge/vision/gdml/GdmlOptimizer.kt b/visionforge-gdml/src/commonMain/kotlin/hep/dataforge/vision/gdml/GdmlOptimizer.kt index 692c334b..bec3822c 100644 --- a/visionforge-gdml/src/commonMain/kotlin/hep/dataforge/vision/gdml/GdmlOptimizer.kt +++ b/visionforge-gdml/src/commonMain/kotlin/hep/dataforge/vision/gdml/GdmlOptimizer.kt @@ -2,16 +2,10 @@ package hep.dataforge.vision.gdml import hep.dataforge.meta.DFExperimental import hep.dataforge.meta.sequence -import hep.dataforge.meta.set -import hep.dataforge.names.Name -import hep.dataforge.names.toName -import hep.dataforge.vision.* +import hep.dataforge.vision.Vision +import hep.dataforge.vision.ownProperties +import hep.dataforge.vision.properties import hep.dataforge.vision.solid.* -import hep.dataforge.vision.visitor.VisionVisitor -import kotlinx.coroutines.CompletableDeferred -import kotlinx.coroutines.Job -import kotlinx.coroutines.coroutineScope -import mu.KotlinLogging public expect class Counter() { public fun get(): Int @@ -24,6 +18,7 @@ private fun Point3D?.safePlus(other: Point3D?): Point3D? = if (this == null && o (this ?: Point3D(0, 0, 0)) + (other ?: Point3D(0, 0, 0)) } +@DFExperimental internal fun Vision.updateFrom(other: Vision): Vision { if (this is Solid && other is Solid) { position = position.safePlus(other.position) @@ -33,9 +28,9 @@ internal fun Vision.updateFrom(other: Vision): Vision { scaleY = scaleY.toDouble() * other.scaleY.toDouble() scaleZ = scaleZ.toDouble() * other.scaleZ.toDouble() } - other.properties?.sequence()?.forEach { (name, item) -> - if (properties?.getItem(name) == null) { - config[name] = item + other.ownProperties?.sequence()?.forEach { (name, item) -> + if (properties.getItem(name) == null) { + setProperty(name, item) } } } diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Composite.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Composite.kt index 9264ee99..dacc79bd 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Composite.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Composite.kt @@ -1,4 +1,3 @@ - package hep.dataforge.vision.solid import hep.dataforge.meta.update @@ -18,7 +17,7 @@ public enum class CompositeType { public class Composite( public val compositeType: CompositeType, public val first: Solid, - public val second: Solid + public val second: Solid, ) : SolidBase(), Solid, VisionGroup { init { @@ -34,15 +33,13 @@ public class Composite( public inline fun VisionContainerBuilder.composite( type: CompositeType, name: String = "", - builder: SolidGroup.() -> Unit + builder: SolidGroup.() -> Unit, ): Composite { val group = SolidGroup().apply(builder) val children = group.children.values.filterIsInstance() if (children.size != 2) error("Composite requires exactly two children") return Composite(type, children[0], children[1]).also { - it.config.update(group.config) - //it.material = group.material - + it.configure { update(group.ownProperties) } if (group.position != null) { it.position = group.position } @@ -65,5 +62,8 @@ public inline fun VisionContainerBuilder.subtract(name: String = "", buil composite(CompositeType.SUBTRACT, name, builder = builder) @VisionBuilder -public inline fun VisionContainerBuilder.intersect(name: String = "", builder: SolidGroup.() -> Unit): Composite = +public inline fun VisionContainerBuilder.intersect( + name: String = "", + builder: SolidGroup.() -> Unit, +): Composite = composite(CompositeType.INTERSECT, name, builder = builder) \ No newline at end of file diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Solid.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Solid.kt index 278cc9e7..a342c961 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Solid.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Solid.kt @@ -1,18 +1,17 @@ package hep.dataforge.vision.solid -import hep.dataforge.meta.* +import hep.dataforge.meta.boolean import hep.dataforge.meta.descriptors.NodeDescriptor +import hep.dataforge.meta.enum +import hep.dataforge.meta.int import hep.dataforge.names.Name import hep.dataforge.names.asName import hep.dataforge.names.plus import hep.dataforge.values.ValueType import hep.dataforge.values.asValue -import hep.dataforge.vision.Vision +import hep.dataforge.vision.* import hep.dataforge.vision.Vision.Companion.VISIBLE_KEY -import hep.dataforge.vision.VisionBuilder -import hep.dataforge.vision.enum import hep.dataforge.vision.layout.Output -import hep.dataforge.vision.setProperty import hep.dataforge.vision.solid.Solid.Companion.DETAIL_KEY import hep.dataforge.vision.solid.Solid.Companion.IGNORE_KEY import hep.dataforge.vision.solid.Solid.Companion.LAYER_KEY @@ -90,7 +89,7 @@ public interface Solid : Vision { var result = +(solid.position?.hashCode() ?: 0) result = 31 * result + (solid.rotation?.hashCode() ?: 0) result = 31 * result + (solid.scale?.hashCode() ?: 0) - result = 31 * result + (solid.properties?.hashCode() ?: 0) + result = 31 * result + solid.properties.hashCode() return result } } @@ -100,9 +99,9 @@ public interface Solid : Vision { * Get the layer number this solid belongs to. Return 0 if layer is not defined. */ public var Solid.layer: Int - get() = properties?.getItem(LAYER_KEY).int ?: 0 + get() = properties.getItem(LAYER_KEY).int ?: 0 set(value) { - config[LAYER_KEY] = value.asValue() + setProperty(LAYER_KEY, value) } @VisionBuilder @@ -153,21 +152,21 @@ public var Solid.x: Number get() = position?.x ?: 0f set(value) { position().x = value.toDouble() - propertyChanged(Solid.X_POSITION_KEY) + notifyPropertyChanged(Solid.X_POSITION_KEY) } public var Solid.y: Number get() = position?.y ?: 0f set(value) { position().y = value.toDouble() - propertyChanged(Solid.Y_POSITION_KEY) + notifyPropertyChanged(Solid.Y_POSITION_KEY) } public var Solid.z: Number get() = position?.z ?: 0f set(value) { position().z = value.toDouble() - propertyChanged(Solid.Z_POSITION_KEY) + notifyPropertyChanged(Solid.Z_POSITION_KEY) } private fun Solid.rotation(): Point3D = @@ -177,21 +176,21 @@ public var Solid.rotationX: Number get() = rotation?.x ?: 0f set(value) { rotation().x = value.toDouble() - propertyChanged(Solid.X_ROTATION_KEY) + notifyPropertyChanged(Solid.X_ROTATION_KEY) } public var Solid.rotationY: Number get() = rotation?.y ?: 0f set(value) { rotation().y = value.toDouble() - propertyChanged(Solid.Y_ROTATION_KEY) + notifyPropertyChanged(Solid.Y_ROTATION_KEY) } public var Solid.rotationZ: Number get() = rotation?.z ?: 0f set(value) { rotation().z = value.toDouble() - propertyChanged(Solid.Z_ROTATION_KEY) + notifyPropertyChanged(Solid.Z_ROTATION_KEY) } private fun Solid.scale(): Point3D = @@ -201,19 +200,19 @@ public var Solid.scaleX: Number get() = scale?.x ?: 1f set(value) { scale().x = value.toDouble() - propertyChanged(Solid.X_SCALE_KEY) + notifyPropertyChanged(Solid.X_SCALE_KEY) } public var Solid.scaleY: Number get() = scale?.y ?: 1f set(value) { scale().y = value.toDouble() - propertyChanged(Solid.Y_SCALE_KEY) + notifyPropertyChanged(Solid.Y_SCALE_KEY) } public var Solid.scaleZ: Number get() = scale?.z ?: 1f set(value) { scale().z = value.toDouble() - propertyChanged(Solid.Z_SCALE_KEY) + notifyPropertyChanged(Solid.Z_SCALE_KEY) } \ No newline at end of file diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt index b87cdaab..e4b9f0f7 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt @@ -1,6 +1,6 @@ package hep.dataforge.vision.solid -import hep.dataforge.meta.Config +import hep.dataforge.meta.MetaItem import hep.dataforge.meta.descriptors.NodeDescriptor import hep.dataforge.names.Name import hep.dataforge.names.NameToken @@ -87,7 +87,10 @@ public tailrec fun PrototypeHolder.getPrototype(name: Name): Solid? = prototypes?.get(name) as? Solid ?: (parent as? PrototypeHolder)?.getPrototype(name) @VisionBuilder -public fun VisionContainerBuilder.group(name: Name = Name.EMPTY, action: SolidGroup.() -> Unit = {}): SolidGroup = +public fun VisionContainerBuilder.group( + name: Name = Name.EMPTY, + action: SolidGroup.() -> Unit = {}, +): SolidGroup = SolidGroup().apply(action).also { set(name, it) } /** @@ -104,15 +107,10 @@ internal class Prototypes( children: Map = emptyMap(), ) : VisionGroupBase(), PrototypeHolder { - init { - this.childrenInternal.putAll(children) - } + override var parent: VisionGroup? = null - override var properties: Config? - get() = null - set(_) { - error("Can't define properties for prototypes block") - } + private val _children = HashMap(children) + override val children: Map get() = _children override val prototypes: MutableVisionGroup get() = this @@ -123,6 +121,21 @@ internal class Prototypes( } } + override fun getOwnProperty(name: Name): MetaItem<*>? = null + + override fun getProperty( + name: Name, + inherit: Boolean, + includeStyles: Boolean, + includeDefaults: Boolean, + ): MetaItem<*>? = null + + override fun setProperty(name: Name, item: MetaItem<*>?) { + TODO("Not yet implemented") + } + + override val descriptor: NodeDescriptor? = null + companion object : KSerializer { private val mapSerializer: KSerializer> = diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidManager.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidManager.kt index a01cc961..5f3c59bc 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidManager.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidManager.kt @@ -36,7 +36,7 @@ public class SolidManager(meta: Meta) : AbstractPlugin(meta) { private fun PolymorphicModuleBuilder.solids() { subclass(SolidGroup.serializer()) - subclass(SolidReference.serializer()) + subclass(SolidReferenceGroup.serializer()) subclass(Composite.serializer()) subclass(Tube.serializer()) subclass(Box.serializer()) diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidMaterial.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidMaterial.kt index aebd168d..8cf8755c 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidMaterial.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidMaterial.kt @@ -10,13 +10,10 @@ import hep.dataforge.values.Value import hep.dataforge.values.ValueType import hep.dataforge.values.asValue import hep.dataforge.values.string -import hep.dataforge.vision.Colors -import hep.dataforge.vision.VisionBuilder -import hep.dataforge.vision.setProperty +import hep.dataforge.vision.* import hep.dataforge.vision.solid.SolidMaterial.Companion.MATERIAL_COLOR_KEY import hep.dataforge.vision.solid.SolidMaterial.Companion.MATERIAL_KEY import hep.dataforge.vision.solid.SolidMaterial.Companion.MATERIAL_OPACITY_KEY -import hep.dataforge.vision.widgetType @VisionBuilder public class ColorAccessor(private val parent: MutableItemProvider, private val colorKey: Name) { @@ -115,7 +112,7 @@ public class SolidMaterial : Scheme() { } } -public val Solid.color: ColorAccessor get() = ColorAccessor(config, MATERIAL_COLOR_KEY) +public val Solid.color: ColorAccessor get() = ColorAccessor(properties, MATERIAL_COLOR_KEY) public var Solid.material: SolidMaterial? get() = getProperty(MATERIAL_KEY).node?.let { SolidMaterial.read(it) } @@ -123,11 +120,11 @@ public var Solid.material: SolidMaterial? @VisionBuilder public fun Solid.material(builder: SolidMaterial.() -> Unit) { - val node = config[MATERIAL_KEY].node + val node = properties.getItem(MATERIAL_KEY).node if (node != null) { SolidMaterial.update(node, builder) } else { - config[MATERIAL_KEY] = SolidMaterial(builder) + setProperty(MATERIAL_KEY, SolidMaterial(builder)) } } diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidReference.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidReference.kt deleted file mode 100644 index 4df85142..00000000 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidReference.kt +++ /dev/null @@ -1,156 +0,0 @@ -package hep.dataforge.vision.solid - -import hep.dataforge.meta.* -import hep.dataforge.meta.descriptors.NodeDescriptor -import hep.dataforge.names.* -import hep.dataforge.vision.* -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable -import kotlinx.serialization.Transient -import kotlin.collections.set - -public abstract class AbstractReference : SolidBase(), VisionGroup { - public abstract val prototype: Solid - - override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? = sequence { - yield(properties?.get(name)) - yieldAll(getStyleItems(name)) - yield(prototype.getProperty(name)) - if (inherit) { - yield(parent?.getProperty(name, inherit)) - } - }.merge() - - override var styles: List - get() = (properties[Vision.STYLE_KEY]?.stringList ?: emptyList()) + prototype.styles - set(value) { - config[Vision.STYLE_KEY] = value - } - - override val allProperties: Laminate - get() = Laminate( - properties, - allStyles, - prototype.allProperties, - parent?.allProperties, - ) - - override fun attachChildren() { - //do nothing - } - - override val descriptor: NodeDescriptor get() = prototype.descriptor -} - -/** - * A reference [Solid] to reuse a template object - */ -@Serializable -@SerialName("solid.ref") -public class SolidReference( - public val templateName: Name, -) : AbstractReference(), Solid { - - /** - * Recursively search for defined template in the parent - */ - override val prototype: Solid - get() = (parent as? SolidGroup)?.getPrototype(templateName) - ?: error("Prototype with name $templateName not found in $parent") - - @Transient - private val propertyCache: HashMap = HashMap() - - - override val children: Map - get() = (prototype as? VisionGroup)?.children - ?.filter { !it.key.toString().startsWith("@") } - ?.mapValues { - ReferenceChild(it.key.asName()) - } ?: emptyMap() - - private fun childPropertyName(childName: Name, propertyName: Name): Name { - return NameToken(REFERENCE_CHILD_PROPERTY_PREFIX, childName.toString()) + propertyName - } - - private fun prototypeFor(name: Name): Solid { - return (prototype as? SolidGroup)?.get(name) as? Solid - ?: error("Prototype with name $name not found in $this") - } - - //override fun findAllStyles(): Laminate = Laminate((styles + prototype.styles).mapNotNull { findStyle(it) }) - - /** - * A ProxyChild is created temporarily only to interact with properties, it does not store any values - * (properties are stored in external cache) and created and destroyed on-demand). - */ - public inner class ReferenceChild(public val name: Name) : AbstractReference() { - - override val prototype: Solid get() = prototypeFor(name) - - override val children: Map - get() = (prototype as? VisionGroup)?.children - ?.filter { !it.key.toString().startsWith("@") } - ?.mapValues { (key, _) -> - ReferenceChild(name + key.asName()) - } ?: emptyMap() - - override var properties: Config? - get() = propertyCache[name] - set(value) { - if (value == null) { - propertyCache.remove(name)?.also { - //Removing listener if it is present - removeChangeListener(this@SolidReference) - } - } else { - propertyCache[name] = value.also { - onPropertyChange(this@SolidReference) { propertyName -> - this@SolidReference.propertyChanged(childPropertyName(name, propertyName)) - } - } - } - } - - } - - public companion object { - public const val REFERENCE_CHILD_PROPERTY_PREFIX: String = "@child" - } -} - -/** - * Get a vision prototype if it is a [SolidReference] or vision itself if it is not - */ -public val Vision.prototype: Vision - get() = when (this) { - is AbstractReference -> prototype - else -> this - } - -/** - * Create ref for existing prototype - */ -public fun SolidGroup.ref( - templateName: Name, - name: String = "", -): SolidReference = SolidReference(templateName).also { set(name, it) } - -/** - * Add new [SolidReference] wrapping given object and automatically adding it to the prototypes - */ -public fun SolidGroup.ref( - name: String, - obj: Solid, - templateName: Name = name.toName(), -): SolidReference { - val existing = getPrototype(templateName) - if (existing == null) { - prototypes { - this[templateName] = obj - } - } else if (existing != obj) { - error("Can't add different prototype on top of existing one") - } - return this@ref.ref(templateName, name) -} diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidReferenceGroup.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidReferenceGroup.kt new file mode 100644 index 00000000..efc7b1cb --- /dev/null +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidReferenceGroup.kt @@ -0,0 +1,189 @@ +package hep.dataforge.vision.solid + +import hep.dataforge.meta.* +import hep.dataforge.meta.descriptors.NodeDescriptor +import hep.dataforge.names.* +import hep.dataforge.vision.* +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.map +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +public interface SolidReference : Vision { + public val prototype: Solid +} + +/** + * A reference [Solid] to reuse a template object + */ +@Serializable +@SerialName("solid.ref") +public class SolidReferenceGroup( + public val templateName: Name, +) : SolidBase(), SolidReference, VisionGroup { + + /** + * Recursively search for defined template in the parent + */ + override val prototype: Solid + get() = (parent as? SolidGroup)?.getPrototype(templateName) + ?: error("Prototype with name $templateName not found in $parent") + + override val children: Map + get() = (prototype as? VisionGroup)?.children + ?.filter { !it.key.toString().startsWith("@") } + ?.mapValues { + ReferenceChild(it.key.asName()) + } ?: emptyMap() + + private fun childToken(childName: Name): NameToken = + NameToken(REFERENCE_CHILD_PROPERTY_PREFIX, childName.toString()) + + private fun childPropertyName(childName: Name, propertyName: Name): Name = + childToken(childName) + propertyName + + private fun getChildProperty(childName: Name, propertyName: Name): MetaItem<*>? { + return getOwnProperty(childPropertyName(childName, propertyName)) + } + + private fun setChildProperty(childName: Name, propertyName: Name, item: MetaItem<*>?) { + setProperty(childPropertyName(childName, propertyName), item) + } + + private fun prototypeFor(name: Name): Solid { + return if(name.isEmpty()) prototype else { + (prototype as? SolidGroup)?.get(name) as? Solid + ?: error("Prototype with name $name not found in $this") + } + } + + override fun getProperty( + name: Name, + inherit: Boolean, + includeStyles: Boolean, + includeDefaults: Boolean, + ): MetaItem<*>? = sequence { + yield(getOwnProperty(name)) + if (includeStyles) { + yieldAll(getStyleItems(name)) + } + yield(prototype.getProperty(name, inherit, includeStyles, includeDefaults)) + if (inherit) { + yield(parent?.getProperty(name, inherit)) + } + }.merge() + + override fun attachChildren() { + //do nothing + } + + override val descriptor: NodeDescriptor get() = prototype.descriptor + + + /** + * A ProxyChild is created temporarily only to interact with properties, it does not store any values + * (properties are stored in external cache) and created and destroyed on-demand). + */ + private inner class ReferenceChild(private val childName: Name) : SolidReference, VisionGroup { + + override val prototype: Solid get() = prototypeFor(childName) + + override val children: Map + get() = (prototype as? VisionGroup)?.children + ?.filter { !it.key.toString().startsWith("@") } + ?.mapValues { (key, _) -> + ReferenceChild(childName + key.asName()) + } ?: emptyMap() + + override fun getOwnProperty(name: Name): MetaItem<*>? = getChildProperty(childName, name) + + override fun setProperty(name: Name, item: MetaItem<*>?) { + setChildProperty(childName, name, item) + } + + override fun getProperty( + name: Name, + inherit: Boolean, + includeStyles: Boolean, + includeDefaults: Boolean, + ): MetaItem<*>? = sequence { + yield(getOwnProperty(name)) + if (includeStyles) { + yieldAll(getStyleItems(name)) + } + yield(prototype.getProperty(name, inherit, includeStyles, includeDefaults)) + if (inherit) { + yield(parent?.getProperty(name, inherit)) + } + }.merge() + + override var parent: VisionGroup? + get(){ + val parentName = childName.cutLast() + return if( parentName.isEmpty()) this@SolidReferenceGroup else ReferenceChild(parentName) + } + set(value) { + error("Setting a parent for a reference child is not possible") + } + + override val propertyInvalidated: Flow + get() = this@SolidReferenceGroup.propertyInvalidated.filter { name -> + name.startsWith(childToken(childName)) + }.map { name -> + name.cutFirst() + } + + override fun notifyPropertyChanged(propertyName: Name) { + this@SolidReferenceGroup.notifyPropertyChanged(childPropertyName(childName, propertyName)) + } + + override fun update(change: VisionChange) { + TODO("Not yet implemented") + } + + override fun attachChildren() { + //do nothing + } + + override val descriptor: NodeDescriptor get() = prototype.descriptor + + } + + public companion object { + public const val REFERENCE_CHILD_PROPERTY_PREFIX: String = "@child" + } +} + +/** + * Get a vision prototype if it is a [SolidReferenceGroup] or vision itself if it is not + */ +public val Vision.prototype: Vision + get() = if (this is SolidReference) prototype else this + +/** + * Create ref for existing prototype + */ +public fun SolidGroup.ref( + templateName: Name, + name: String = "", +): SolidReferenceGroup = SolidReferenceGroup(templateName).also { set(name, it) } + +/** + * Add new [SolidReferenceGroup] wrapping given object and automatically adding it to the prototypes + */ +public fun SolidGroup.ref( + name: String, + obj: Solid, + templateName: Name = name.toName(), +): SolidReferenceGroup { + val existing = getPrototype(templateName) + if (existing == null) { + prototypes { + this[templateName] = obj + } + } else if (existing != obj) { + error("Can't add different prototype on top of existing one") + } + return this@ref.ref(templateName, name) +} diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/transform/RemoveSingleChild.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/transform/RemoveSingleChild.kt index ff879d7e..0b64a7b7 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/transform/RemoveSingleChild.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/transform/RemoveSingleChild.kt @@ -1,18 +1,15 @@ package hep.dataforge.vision.solid.transform import hep.dataforge.meta.DFExperimental -import hep.dataforge.meta.update import hep.dataforge.names.asName -import hep.dataforge.vision.MutableVisionGroup -import hep.dataforge.vision.Vision -import hep.dataforge.vision.VisionGroup +import hep.dataforge.vision.* import hep.dataforge.vision.solid.* @DFExperimental internal fun mergeChild(parent: VisionGroup, child: Vision): Vision { return child.apply { - config.update(parent.config) + configure(parent.ownProperties) //parent.properties?.let { config.update(it) } @@ -40,7 +37,7 @@ internal object RemoveSingleChild : VisualTreeTransform() { override fun SolidGroup.transformInPlace() { fun MutableVisionGroup.replaceChildren() { children.forEach { (childName, parent) -> - if (parent is SolidReference) return@forEach //ignore refs + if (parent is SolidReferenceGroup) return@forEach //ignore refs if (parent is MutableVisionGroup) { parent.replaceChildren() } diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/transform/UnRef.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/transform/UnRef.kt index d45c2ca2..cb0ac4e2 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/transform/UnRef.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/transform/UnRef.kt @@ -6,7 +6,7 @@ import hep.dataforge.names.asName import hep.dataforge.vision.MutableVisionGroup import hep.dataforge.vision.VisionGroup import hep.dataforge.vision.solid.SolidGroup -import hep.dataforge.vision.solid.SolidReference +import hep.dataforge.vision.solid.SolidReferenceGroup @DFExperimental internal object UnRef : VisualTreeTransform() { @@ -17,7 +17,7 @@ internal object UnRef : VisualTreeTransform() { counter.forEach { (key, value) -> reducer[key] = (reducer[key] ?: 0) + value } - } else if (obj is SolidReference) { + } else if (obj is SolidReferenceGroup) { reducer[obj.templateName] = (reducer[obj.templateName] ?: 0) + 1 } @@ -27,8 +27,8 @@ internal object UnRef : VisualTreeTransform() { private fun MutableVisionGroup.unref(name: Name) { (this as? SolidGroup)?.prototypes?.set(name, null) - children.filter { (it.value as? SolidReference)?.templateName == name }.forEach { (key, value) -> - val reference = value as SolidReference + children.filter { (it.value as? SolidReferenceGroup)?.templateName == name }.forEach { (key, value) -> + val reference = value as SolidReferenceGroup val newChild = mergeChild(reference, reference.prototype) newChild.parent = null set(key.asName(), newChild) // replace proxy with merged object diff --git a/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/PropertyTest.kt b/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/PropertyTest.kt index 907edf3e..6e045744 100644 --- a/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/PropertyTest.kt +++ b/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/PropertyTest.kt @@ -1,8 +1,8 @@ package hep.dataforge.vision.solid import hep.dataforge.meta.int -import hep.dataforge.meta.set import hep.dataforge.names.asName +import hep.dataforge.vision.setProperty import hep.dataforge.vision.styleSheet import hep.dataforge.vision.useStyle import kotlin.test.Test @@ -14,7 +14,7 @@ class PropertyTest { fun testInheritedProperty() { var box: Box? = null val group = SolidGroup().apply { - config["test"] = 22 + setProperty("test", 22) group { box = box(100, 100, 100) } @@ -60,7 +60,7 @@ class PropertyTest { @Test fun testReferenceStyleProperty() { - var box: SolidReference? = null + var box: SolidReferenceGroup? = null val group = SolidGroup{ styleSheet { set("testStyle") { diff --git a/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/SerializationTest.kt b/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/SerializationTest.kt index efef7fcd..017d4db3 100644 --- a/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/SerializationTest.kt +++ b/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/SerializationTest.kt @@ -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.ownProperties import kotlin.test.Test import kotlin.test.assertEquals @@ -15,7 +16,7 @@ fun SolidGroup.refGroup( name: String, templateName: Name = name.toName(), block: MutableVisionGroup.() -> Unit -): SolidReference { +): SolidReferenceGroup { val group = SolidGroup().apply(block) return ref(name, group, templateName) } @@ -32,7 +33,7 @@ class SerializationTest { val string = SolidManager.encodeToString(cube) println(string) val newCube = SolidManager.decodeFromString(string) - assertEquals(cube.config, newCube.config) + assertEquals(cube.ownProperties, newCube.ownProperties) } @Test @@ -53,7 +54,7 @@ class SerializationTest { val string = SolidManager.encodeToString(group) println(string) val reconstructed = SolidManager.decodeFromString(string) as SolidGroup - assertEquals(group["cube"]?.config, reconstructed["cube"]?.config) + assertEquals(group["cube"]?.ownProperties, reconstructed["cube"]?.ownProperties) } } \ No newline at end of file diff --git a/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/VisionUpdateTest.kt b/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/VisionUpdateTest.kt index f5ff4f14..79bba867 100644 --- a/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/VisionUpdateTest.kt +++ b/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/VisionUpdateTest.kt @@ -45,7 +45,7 @@ class VisionUpdateTest { val serialized = visionManager.jsonFormat.encodeToString(VisionChange.serializer(), change) println(serialized) val reconstructed = visionManager.jsonFormat.decodeFromString(VisionChange.serializer(), serialized) - assertEquals(change.propertyChange,reconstructed.propertyChange) + assertEquals(change.properties,reconstructed.properties) } @Test diff --git a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/MeshThreeFactory.kt b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/MeshThreeFactory.kt index 44478c61..519fa9b3 100644 --- a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/MeshThreeFactory.kt +++ b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/MeshThreeFactory.kt @@ -15,20 +15,22 @@ 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 kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import kotlin.reflect.KClass /** * Basic geometry-based factory */ public abstract class MeshThreeFactory( - override val type: KClass + override val type: KClass, ) : ThreeFactory { /** * Build a geometry for an object */ public abstract fun buildGeometry(obj: T): BufferGeometry - override fun invoke(obj: T): Mesh { + override fun invoke(three: ThreePlugin, obj: T): Mesh { val geometry = buildGeometry(obj) //JS sometimes tries to pass Geometry as BufferGeometry @@ -36,14 +38,14 @@ public abstract class MeshThreeFactory( //val meshMeta: Meta = obj.properties[Material3D.MATERIAL_KEY]?.node ?: Meta.empty - val mesh = Mesh(geometry, null).apply{ + val mesh = Mesh(geometry, null).apply { matrixAutoUpdate = false //set position for mesh updatePosition(obj) }.applyProperties(obj) //add listener to object properties - obj.onPropertyChange(this) { name -> + obj.propertyInvalidated.onEach { name -> when { name.startsWith(Solid.GEOMETRY_KEY) -> { val oldGeometry = mesh.geometry as BufferGeometry @@ -57,7 +59,8 @@ public abstract class MeshThreeFactory( name.startsWith(EDGES_KEY) -> mesh.applyEdges(obj) else -> mesh.updateProperty(obj, name) } - } + }.launchIn(three.updateScope) + return mesh } @@ -72,7 +75,7 @@ public abstract class MeshThreeFactory( } } -fun Mesh.applyProperties(obj: Solid): Mesh = apply{ +internal fun Mesh.applyProperties(obj: Solid): Mesh = apply { material = getMaterial(obj, true) applyEdges(obj) applyWireFrame(obj) @@ -82,7 +85,7 @@ fun Mesh.applyProperties(obj: Solid): Mesh = apply{ } } -fun Mesh.applyEdges(obj: Solid) { +internal fun Mesh.applyEdges(obj: Solid) { val edges = children.find { it.name == "@edges" } as? LineSegments //inherited edges definition, enabled by default if (obj.getProperty(MeshThreeFactory.EDGES_ENABLED_KEY).boolean != false) { @@ -108,7 +111,7 @@ fun Mesh.applyEdges(obj: Solid) { } } -fun Mesh.applyWireFrame(obj: Solid) { +internal fun Mesh.applyWireFrame(obj: Solid) { children.find { it.name == "@wireframe" }?.let { remove(it) (it as LineSegments).dispose() @@ -116,7 +119,8 @@ fun Mesh.applyWireFrame(obj: Solid) { //inherited wireframe definition, disabled by default if (obj.getProperty(MeshThreeFactory.WIREFRAME_ENABLED_KEY).boolean == true) { val bufferGeometry = geometry as? BufferGeometry ?: return - val material = ThreeMaterials.getLineMaterial(obj.getProperty(MeshThreeFactory.WIREFRAME_MATERIAL_KEY).node, true) + val material = + ThreeMaterials.getLineMaterial(obj.getProperty(MeshThreeFactory.WIREFRAME_MATERIAL_KEY).node, true) add( LineSegments( WireframeGeometry(bufferGeometry), diff --git a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeConvexFactory.kt b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeConvexFactory.kt index 0f5160f8..e41035c9 100644 --- a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeConvexFactory.kt +++ b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeConvexFactory.kt @@ -2,11 +2,10 @@ package hep.dataforge.vision.solid.three import hep.dataforge.vision.solid.Convex import info.laht.threekt.external.geometries.ConvexBufferGeometry -import info.laht.threekt.math.Vector3 public object ThreeConvexFactory : MeshThreeFactory(Convex::class) { override fun buildGeometry(obj: Convex): ConvexBufferGeometry { - @Suppress("USELESS_CAST") val vectors = obj.points.toTypedArray() as Array + val vectors = obj.points.map { it.toVector() }.toTypedArray() return ConvexBufferGeometry(vectors) } } \ No newline at end of file diff --git a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeFactory.kt b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeFactory.kt index d01eed4c..33cfcc60 100644 --- a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeFactory.kt +++ b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeFactory.kt @@ -22,7 +22,7 @@ public interface ThreeFactory { public val type: KClass - public operator fun invoke(obj: T): Object3D + public operator fun invoke(three: ThreePlugin, obj: T): Object3D public companion object { public const val TYPE: String = "threeFactory" diff --git a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeLabelFactory.kt b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeLabelFactory.kt index ecea5d23..9ab3f401 100644 --- a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeLabelFactory.kt +++ b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeLabelFactory.kt @@ -1,12 +1,15 @@ package hep.dataforge.vision.solid.three +import hep.dataforge.context.logger import hep.dataforge.vision.solid.SolidLabel import hep.dataforge.vision.solid.three.ThreeMaterials.getMaterial import info.laht.threekt.core.Object3D import info.laht.threekt.geometries.TextBufferGeometry import info.laht.threekt.objects.Mesh import kotlinext.js.jsObject +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import kotlin.reflect.KClass /** @@ -15,18 +18,19 @@ import kotlin.reflect.KClass public object ThreeLabelFactory : ThreeFactory { override val type: KClass get() = SolidLabel::class - override fun invoke(obj: SolidLabel): Object3D { + override fun invoke(three: ThreePlugin, obj: SolidLabel): Object3D { val textGeo = TextBufferGeometry(obj.text, jsObject { font = obj.fontFamily size = 20 height = 1 curveSegments = 1 }) - return Mesh(textGeo, getMaterial(obj,true)).apply { + return Mesh(textGeo, getMaterial(obj, true)).apply { updatePosition(obj) - obj.onPropertyChange(this@ThreeLabelFactory) { _ -> + obj.propertyInvalidated.onEach { _ -> //TODO - } + three.logger.warn{"Label parameter change not implemented"} + }.launchIn(three.updateScope) } } } \ No newline at end of file diff --git a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeLineFactory.kt b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeLineFactory.kt index b5ec871e..199213ba 100644 --- a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeLineFactory.kt +++ b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeLineFactory.kt @@ -9,12 +9,14 @@ import info.laht.threekt.core.Geometry import info.laht.threekt.core.Object3D import info.laht.threekt.math.Color import info.laht.threekt.objects.LineSegments +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import kotlin.reflect.KClass public object ThreeLineFactory : ThreeFactory { override val type: KClass get() = PolyLine::class - override fun invoke(obj: PolyLine): Object3D { + override fun invoke(three: ThreePlugin, obj: PolyLine): Object3D { val geometry = Geometry().apply { vertices = Array(obj.points.size) { obj.points[it].toVector() } } @@ -28,9 +30,9 @@ public object ThreeLineFactory : ThreeFactory { updatePosition(obj) //layers.enable(obj.layer) //add listener to object properties - obj.onPropertyChange(this) { propertyName -> + obj.propertyInvalidated.onEach { propertyName -> updateProperty(obj, propertyName) - } + }.launchIn(three.updateScope) } } diff --git a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreePlugin.kt b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreePlugin.kt index bcf309bb..9eacdea9 100644 --- a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreePlugin.kt +++ b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreePlugin.kt @@ -9,6 +9,9 @@ import hep.dataforge.vision.solid.* import hep.dataforge.vision.solid.specifications.Canvas3DOptions import hep.dataforge.vision.visible import info.laht.threekt.core.Object3D +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import org.w3c.dom.Element import org.w3c.dom.HTMLElement import kotlin.collections.set @@ -24,6 +27,9 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer { private val compositeFactory = ThreeCompositeFactory(this) private val refFactory = ThreeReferenceFactory(this) + //TODO generate a separate supervisor update scope + internal val updateScope: CoroutineScope get() = context + init { //Add specialized factories here objectFactories[Box::class] = ThreeBoxFactory @@ -44,7 +50,7 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer { public fun buildObject3D(obj: Solid): Object3D { return when (obj) { is ThreeVision -> obj.render() - is SolidReference -> refFactory(obj) + is SolidReferenceGroup -> refFactory(obj) is SolidGroup -> { val group = ThreeGroup() obj.children.forEach { (token, child) -> @@ -63,7 +69,7 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer { updatePosition(obj) //obj.onChildrenChange() - obj.onPropertyChange(this) { name -> + obj.propertyInvalidated.onEach { name -> if ( name.startsWith(Solid.POSITION_KEY) || name.startsWith(Solid.ROTATION) || @@ -74,9 +80,9 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer { } else if (name == Vision.VISIBLE_KEY) { visible = obj.visible ?: true } - } + }.launchIn(updateScope) - obj.onStructureChange(this) { nameToken, _, child -> + obj.structureChanges.onEach { (nameToken, _, child) -> // if (name.isEmpty()) { // logger.error { "Children change with empty name on $group" } // return@onChildrenChange @@ -99,7 +105,7 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer { logger.error(ex) { "Failed to render $child" } } } - } + }.launchIn(updateScope) } } is Composite -> compositeFactory(obj) diff --git a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeReferenceFactory.kt b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeReferenceFactory.kt index 32ea564f..02739b1c 100644 --- a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeReferenceFactory.kt +++ b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeReferenceFactory.kt @@ -4,17 +4,19 @@ import hep.dataforge.names.cutFirst import hep.dataforge.names.firstOrNull import hep.dataforge.names.toName import hep.dataforge.vision.solid.Solid -import hep.dataforge.vision.solid.SolidReference -import hep.dataforge.vision.solid.SolidReference.Companion.REFERENCE_CHILD_PROPERTY_PREFIX +import hep.dataforge.vision.solid.SolidReferenceGroup +import hep.dataforge.vision.solid.SolidReferenceGroup.Companion.REFERENCE_CHILD_PROPERTY_PREFIX import info.laht.threekt.core.BufferGeometry import info.laht.threekt.core.Object3D import info.laht.threekt.objects.Mesh +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import kotlin.reflect.KClass -public class ThreeReferenceFactory(public val three: ThreePlugin) : ThreeFactory { +public class ThreeReferenceFactory(public val three: ThreePlugin) : ThreeFactory { private val cache = HashMap() - override val type: KClass = SolidReference::class + override val type: KClass = SolidReferenceGroup::class private fun Object3D.replicate(): Object3D { return when (this) { @@ -30,7 +32,7 @@ public class ThreeReferenceFactory(public val three: ThreePlugin) : ThreeFactory } } - override fun invoke(obj: SolidReference): Object3D { + override fun invoke(obj: SolidReferenceGroup): Object3D { val template = obj.prototype val cachedObject = cache.getOrPut(template) { three.buildObject3D(template) @@ -43,7 +45,9 @@ public class ThreeReferenceFactory(public val three: ThreePlugin) : ThreeFactory object3D.applyProperties(obj) } - obj.onPropertyChange(this) { name -> + //TODO apply child properties + + obj.propertyInvalidated.onEach { name-> if (name.firstOrNull()?.body == REFERENCE_CHILD_PROPERTY_PREFIX) { val childName = name.firstOrNull()?.index?.toName() ?: error("Wrong syntax for reference child property: '$name'") val propertyName = name.cutFirst() @@ -53,7 +57,8 @@ public class ThreeReferenceFactory(public val three: ThreePlugin) : ThreeFactory } else { object3D.updateProperty(obj, name) } - } + }.launchIn(three.updateScope) + return object3D } From 929832f3a5b1a63ceef4729305309093a2f5e2c7 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 15 Dec 2020 19:37:33 +0300 Subject: [PATCH 02/19] Change property and structure subscription to flows. --- .../hep/dataforge/vision/gdml/demo/GdmlFxDemoApp.kt | 3 ++- .../main/kotlin/hep/dataforge/vision/solid/FX3DPlugin.kt | 2 +- .../hep/dataforge/vision/solid/FXReferenceFactory.kt | 6 ++++-- .../hep/dataforge/vision/solid/VisualObjectFXBinding.kt | 8 +++++--- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/demo/gdml/src/jvmMain/kotlin/hep/dataforge/vision/gdml/demo/GdmlFxDemoApp.kt b/demo/gdml/src/jvmMain/kotlin/hep/dataforge/vision/gdml/demo/GdmlFxDemoApp.kt index 1d5a4a89..03915243 100644 --- a/demo/gdml/src/jvmMain/kotlin/hep/dataforge/vision/gdml/demo/GdmlFxDemoApp.kt +++ b/demo/gdml/src/jvmMain/kotlin/hep/dataforge/vision/gdml/demo/GdmlFxDemoApp.kt @@ -2,6 +2,7 @@ package hep.dataforge.vision.gdml.demo import hep.dataforge.context.Global import hep.dataforge.vision.VisionManager +import hep.dataforge.vision.describedProperties import hep.dataforge.vision.editor.VisualObjectEditorFragment import hep.dataforge.vision.editor.VisualObjectTreeFragment import hep.dataforge.vision.gdml.toVision @@ -26,7 +27,7 @@ class GDMLView : View() { } private val propertyEditor = VisualObjectEditorFragment { - it.allProperties + it.describedProperties }.apply { descriptorProperty.set(SolidMaterial.descriptor) itemProperty.bind(treeFragment.selectedProperty) diff --git a/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/FX3DPlugin.kt b/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/FX3DPlugin.kt index ce88a14c..b635485b 100644 --- a/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/FX3DPlugin.kt +++ b/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/FX3DPlugin.kt @@ -43,7 +43,7 @@ class FX3DPlugin : AbstractPlugin() { } fun buildNode(obj: Solid): Node { - val binding = VisualObjectFXBinding(obj) + val binding = VisualObjectFXBinding(this, obj) return when (obj) { is SolidReferenceGroup -> referenceFactory(obj, binding) is SolidGroup -> { diff --git a/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/FXReferenceFactory.kt b/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/FXReferenceFactory.kt index 77244de2..2ffb2660 100644 --- a/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/FXReferenceFactory.kt +++ b/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/FXReferenceFactory.kt @@ -4,6 +4,8 @@ import hep.dataforge.names.* import hep.dataforge.vision.Vision import javafx.scene.Group import javafx.scene.Node +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import kotlin.reflect.KClass class FXReferenceFactory(val plugin: FX3DPlugin) : FX3DFactory { @@ -13,7 +15,7 @@ class FXReferenceFactory(val plugin: FX3DPlugin) : FX3DFactory + obj.propertyInvalidated.onEach { name-> if (name.firstOrNull()?.body == SolidReferenceGroup.REFERENCE_CHILD_PROPERTY_PREFIX) { val childName = name.firstOrNull()?.index?.toName() ?: error("Wrong syntax for reference child property: '$name'") val propertyName = name.cutFirst() @@ -21,7 +23,7 @@ class FXReferenceFactory(val plugin: FX3DPlugin) : FX3DFactory?>>() init { - obj.onPropertyChange(this) { name -> + obj.propertyInvalidated.onEach { name -> bindings.filter { it.key.startsWith(name) }.forEach { entry -> Platform.runLater { entry.value.invalidate() @@ -28,7 +30,7 @@ class VisualObjectFXBinding(val obj: Vision) { // bindings[currentName]?.invalidate() // currentName = currentName.cutLast() // } - } + }.launchIn(fx.context) } operator fun get(key: Name): ObjectBinding?> { From 6a6d9659cac7ebc3f606877051e5401afe1e4274 Mon Sep 17 00:00:00 2001 From: darksnake Date: Wed, 16 Dec 2020 13:24:38 +0300 Subject: [PATCH 03/19] Fixing new property update model --- build.gradle.kts | 2 +- .../vision/solid/demo/VariableBox.kt | 17 ++++++++++------- .../vision/bootstrap/threeControls.kt | 3 ++- .../vision/bootstrap/visionPropertyEditor.kt | 3 ++- .../dataforge/vision/react/ConfigEditor.kt | 10 +++++----- .../kotlin/hep/dataforge/vision/VisionBase.kt | 19 +++++++++---------- .../hep/dataforge/vision/VisionGroupBase.kt | 5 +---- .../dataforge/vision/client/VisionClient.kt | 8 ++++++-- .../hep/dataforge/vision/solid/Solid.kt | 2 +- .../hep/dataforge/vision/solid/SolidGroup.kt | 8 ++++---- ...lidReferenceGroup.kt => SolidReference.kt} | 0 .../dataforge/vision/solid/PropertyTest.kt | 5 +++-- .../vision/solid/three/MeshThreeFactory.kt | 4 ++-- .../solid/three/ThreeCanvasLabelFactory.kt | 2 +- .../vision/solid/three/ThreePlugin.kt | 11 +++++------ .../solid/three/ThreeReferenceFactory.kt | 4 ++-- .../vision/solid/three/ThreeVision.kt | 2 +- 17 files changed, 55 insertions(+), 50 deletions(-) rename visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/{SolidReferenceGroup.kt => SolidReference.kt} (100%) diff --git a/build.gradle.kts b/build.gradle.kts index d4147448..38355ee0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,7 +2,7 @@ plugins { id("ru.mipt.npm.project") } -val dataforgeVersion by extra("0.2.1-dev-4") +val dataforgeVersion by extra("0.2.1-dev-5") val ktorVersion by extra("1.4.3") val htmlVersion by extra("0.7.2") val kotlinWrappersVersion by extra("pre.129-kotlin-1.4.20") diff --git a/demo/spatial-showcase/src/jsMain/kotlin/hep/dataforge/vision/solid/demo/VariableBox.kt b/demo/spatial-showcase/src/jsMain/kotlin/hep/dataforge/vision/solid/demo/VariableBox.kt index ff610211..d4e8d833 100644 --- a/demo/spatial-showcase/src/jsMain/kotlin/hep/dataforge/vision/solid/demo/VariableBox.kt +++ b/demo/spatial-showcase/src/jsMain/kotlin/hep/dataforge/vision/solid/demo/VariableBox.kt @@ -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.properties import hep.dataforge.vision.set import hep.dataforge.vision.setProperty import hep.dataforge.vision.solid.* @@ -16,6 +17,8 @@ import info.laht.threekt.core.BufferGeometry import info.laht.threekt.core.Object3D import info.laht.threekt.geometries.BoxBufferGeometry import info.laht.threekt.objects.Mesh +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import kotlin.math.max internal fun SolidGroup.varBox( @@ -31,11 +34,11 @@ internal class VariableBox(xSize: Number, ySize: Number, zSize: Number) : ThreeV scaleX = xSize scaleY = ySize scaleZ = zSize - config[MeshThreeFactory.EDGES_ENABLED_KEY] = false - config[MeshThreeFactory.WIREFRAME_ENABLED_KEY] = false + properties[MeshThreeFactory.EDGES_ENABLED_KEY] = false + properties[MeshThreeFactory.WIREFRAME_ENABLED_KEY] = false } - override fun render(): Object3D { + override fun render(three: ThreePlugin): Object3D { val xSize = getProperty(X_SIZE_KEY, false).number?.toDouble() ?: 1.0 val ySize = getProperty(Y_SIZE_KEY, false).number?.toDouble() ?: 1.0 val zSize = getProperty(Z_SIZE_KEY, false).number?.toDouble() ?: 1.0 @@ -60,7 +63,7 @@ internal class VariableBox(xSize: Number, ySize: Number, zSize: Number) : ThreeV mesh.scale.set(xSize, ySize, zSize) //add listener to object properties - onPropertyChange(mesh) { name -> + propertyInvalidated.onEach { name -> when { name.startsWith(GEOMETRY_KEY) -> { val newXSize = getProperty(X_SIZE_KEY, false).number?.toDouble() ?: 1.0 @@ -71,12 +74,12 @@ internal class VariableBox(xSize: Number, ySize: Number, zSize: Number) : ThreeV } name.startsWith(MeshThreeFactory.WIREFRAME_KEY) -> mesh.applyWireFrame(this@VariableBox) name.startsWith(MeshThreeFactory.EDGES_KEY) -> mesh.applyEdges(this@VariableBox) - name.startsWith(MATERIAL_COLOR_KEY)->{ + name.startsWith(MATERIAL_COLOR_KEY) -> { mesh.material = getMaterial(this, true) } else -> mesh.updateProperty(this@VariableBox, name) } - } + }.launchIn(three.context) return mesh } @@ -100,7 +103,7 @@ internal class VariableBox(xSize: Number, ySize: Number, zSize: Number) : ThreeV color(r.toUByte(), g.toUByte(), b.toUByte()) } - companion object{ + companion object { private val X_SIZE_KEY = GEOMETRY_KEY + "xSize" private val Y_SIZE_KEY = GEOMETRY_KEY + "ySize" private val Z_SIZE_KEY = GEOMETRY_KEY + "zSize" diff --git a/ui/bootstrap/src/main/kotlin/hep/dataforge/vision/bootstrap/threeControls.kt b/ui/bootstrap/src/main/kotlin/hep/dataforge/vision/bootstrap/threeControls.kt index 68d90705..ea835f4f 100644 --- a/ui/bootstrap/src/main/kotlin/hep/dataforge/vision/bootstrap/threeControls.kt +++ b/ui/bootstrap/src/main/kotlin/hep/dataforge/vision/bootstrap/threeControls.kt @@ -4,6 +4,7 @@ import hep.dataforge.names.Name import hep.dataforge.names.isEmpty import hep.dataforge.vision.Vision import hep.dataforge.vision.VisionGroup +import hep.dataforge.vision.describedProperties import hep.dataforge.vision.react.objectTree import hep.dataforge.vision.solid.three.ThreeCanvas import kotlinx.css.* @@ -53,7 +54,7 @@ public val ThreeControls: FunctionalComponent = functionalCo if (selectedObject != null) { visionPropertyEditor( selectedObject, - default = selectedObject.allProperties, + default = selectedObject.describedProperties, key = selected ) } diff --git a/ui/bootstrap/src/main/kotlin/hep/dataforge/vision/bootstrap/visionPropertyEditor.kt b/ui/bootstrap/src/main/kotlin/hep/dataforge/vision/bootstrap/visionPropertyEditor.kt index 5bc10577..9734bc74 100644 --- a/ui/bootstrap/src/main/kotlin/hep/dataforge/vision/bootstrap/visionPropertyEditor.kt +++ b/ui/bootstrap/src/main/kotlin/hep/dataforge/vision/bootstrap/visionPropertyEditor.kt @@ -4,6 +4,7 @@ import hep.dataforge.meta.Meta import hep.dataforge.meta.descriptors.NodeDescriptor import hep.dataforge.vision.Vision import hep.dataforge.vision.getStyle +import hep.dataforge.vision.properties import hep.dataforge.vision.react.configEditor import hep.dataforge.vision.react.metaViewer import org.w3c.dom.Element @@ -17,7 +18,7 @@ public fun RBuilder.visionPropertyEditor( key: Any? = null ) { card("Properties") { - configEditor(item.config, descriptor, default, key) + configEditor(item.properties, descriptor, default, key) } val styles = item.styles if(styles.isNotEmpty()) { diff --git a/ui/react/src/main/kotlin/hep/dataforge/vision/react/ConfigEditor.kt b/ui/react/src/main/kotlin/hep/dataforge/vision/react/ConfigEditor.kt index a4ecce5e..b659452f 100644 --- a/ui/react/src/main/kotlin/hep/dataforge/vision/react/ConfigEditor.kt +++ b/ui/react/src/main/kotlin/hep/dataforge/vision/react/ConfigEditor.kt @@ -19,7 +19,7 @@ public external interface ConfigEditorItemProps : RProps { /** * Root config object - always non null */ - public var root: Config + public var root: MutableItemProvider /** * Full path to the displayed node in [root]. Could be empty @@ -44,7 +44,7 @@ private val ConfigEditorItem: FunctionalComponent = private fun RBuilder.configEditorItem(props: ConfigEditorItemProps) { var expanded: Boolean by useState { true } - var item: MetaItem? by useState { props.root[props.name] } + var item: MetaItem<*>? by useState { props.root.getItem(props.name) } val descriptorItem: ItemDescriptor? = props.descriptor?.get(props.name) val defaultItem = props.default?.get(props.name) var actualItem: MetaItem? by useState { item ?: defaultItem ?: descriptorItem?.defaultItem() } @@ -52,7 +52,7 @@ private fun RBuilder.configEditorItem(props: ConfigEditorItemProps) { val token = props.name.lastOrNull()?.toString() ?: "Properties" fun update() { - item = props.root[props.name] + item = props.root.getItem(props.name) actualItem = item ?: defaultItem ?: descriptorItem?.defaultItem() } @@ -192,7 +192,7 @@ private fun RBuilder.configEditorItem(props: ConfigEditorItemProps) { public external interface ConfigEditorProps : RProps { public var id: Name - public var root: Config + public var root: MutableItemProvider public var default: Meta? public var descriptor: NodeDescriptor? } @@ -229,7 +229,7 @@ public fun Element.configEditor( } public fun RBuilder.configEditor( - config: Config, + config: MutableItemProvider, descriptor: NodeDescriptor? = null, default: Meta? = null, key: Any? = null, diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt index 19080b67..aa1fcba2 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt @@ -8,7 +8,6 @@ import hep.dataforge.names.Name import hep.dataforge.names.asName import hep.dataforge.values.ValueType import hep.dataforge.vision.Vision.Companion.STYLE_KEY -import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.serialization.SerialName @@ -32,32 +31,33 @@ public open class VisionBase : Vision { * Object own properties excluding styles and inheritance */ @SerialName("properties") - private var _properties: Config? = null + protected var innerProperties: Config? = null + private set /** * All own properties as a read-only Meta */ - public val ownProperties: Meta get() = _properties?: Meta.EMPTY + public val ownProperties: Meta get() = innerProperties ?: Meta.EMPTY @Synchronized private fun getOrCreateConfig(): Config { - if (_properties == null) { + if (innerProperties == null) { val newProperties = Config() - _properties = newProperties + innerProperties = newProperties newProperties.onChange(this) { name, oldItem, newItem -> if (oldItem != newItem) { notifyPropertyChanged(name) } } } - return _properties!! + return innerProperties!! } /** * A fast accessor method to get own property (no inheritance or styles */ override fun getOwnProperty(name: Name): MetaItem<*>? { - return _properties?.getItem(name) + return innerProperties?.getItem(name) } override fun getProperty( @@ -93,9 +93,8 @@ public open class VisionBase : Vision { } } - private val _propertyInvalidationFlow: MutableSharedFlow = MutableSharedFlow( - onBufferOverflow = BufferOverflow.DROP_OLDEST - ) + @Transient + private val _propertyInvalidationFlow: MutableSharedFlow = MutableSharedFlow() override val propertyInvalidated: SharedFlow get() = _propertyInvalidationFlow diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionGroupBase.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionGroupBase.kt index a1170b48..0bb40007 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionGroupBase.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionGroupBase.kt @@ -1,7 +1,6 @@ package hep.dataforge.vision import hep.dataforge.names.* -import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.serialization.SerialName @@ -36,9 +35,7 @@ public open class VisionGroupBase : VisionBase(), MutableVisionGroup { } @Transient - private val _structureChanges: MutableSharedFlow = MutableSharedFlow( - onBufferOverflow = BufferOverflow.DROP_OLDEST - ) + private val _structureChanges: MutableSharedFlow = MutableSharedFlow() override val structureChanges: SharedFlow get() = _structureChanges diff --git a/visionforge-core/src/jsMain/kotlin/hep/dataforge/vision/client/VisionClient.kt b/visionforge-core/src/jsMain/kotlin/hep/dataforge/vision/client/VisionClient.kt index 8dda1ebf..eefffc17 100644 --- a/visionforge-core/src/jsMain/kotlin/hep/dataforge/vision/client/VisionClient.kt +++ b/visionforge-core/src/jsMain/kotlin/hep/dataforge/vision/client/VisionClient.kt @@ -18,6 +18,10 @@ import org.w3c.dom.WebSocket import org.w3c.dom.asList import org.w3c.dom.get import org.w3c.dom.url.URL +import kotlin.collections.HashMap +import kotlin.collections.forEach +import kotlin.collections.maxByOrNull +import kotlin.collections.set import kotlin.reflect.KClass public class VisionClient : AbstractPlugin() { @@ -124,9 +128,9 @@ public class VisionClient : AbstractPlugin() { ) logger.debug { "Got update $dif for output with name $name" } visionMap[element]?.update(dif) - ?: logger.info { "Target vision for element $element with name $name not found" } + ?: console.info("Target vision for element $element with name $name not found") } else { - console.error("WebSocket message data is not a string") + console.error ("WebSocket message data is not a string") } } onopen = { diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Solid.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Solid.kt index a342c961..d690c6b1 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Solid.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Solid.kt @@ -81,7 +81,7 @@ public interface Solid : Vision { if (first.position != second.position) return false if (first.rotation != second.rotation) return false if (first.scale != second.scale) return false - if (first.properties != second.properties) return false + if (first.ownProperties != second.ownProperties) return false return true } diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt index e4b9f0f7..cc3b92e4 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt @@ -38,6 +38,7 @@ public class SolidGroup : VisionGroupBase(), Solid, PrototypeHolder { /** * Create or edit prototype node as a group */ + @VisionBuilder public fun prototypes(builder: VisionContainerBuilder.() -> Unit): Unit { (prototypes ?: Prototypes().also { prototypes = it @@ -107,10 +108,9 @@ internal class Prototypes( children: Map = emptyMap(), ) : VisionGroupBase(), PrototypeHolder { - override var parent: VisionGroup? = null - - private val _children = HashMap(children) - override val children: Map get() = _children + init { + childrenInternal.putAll(children) + } override val prototypes: MutableVisionGroup get() = this diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidReferenceGroup.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidReference.kt similarity index 100% rename from visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidReferenceGroup.kt rename to visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidReference.kt diff --git a/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/PropertyTest.kt b/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/PropertyTest.kt index 6e045744..55efe64a 100644 --- a/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/PropertyTest.kt +++ b/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/PropertyTest.kt @@ -2,6 +2,7 @@ package hep.dataforge.vision.solid import hep.dataforge.meta.int import hep.dataforge.names.asName +import hep.dataforge.vision.getProperty import hep.dataforge.vision.setProperty import hep.dataforge.vision.styleSheet import hep.dataforge.vision.useStyle @@ -19,7 +20,7 @@ class PropertyTest { box = box(100, 100, 100) } } - assertEquals(22, box?.getProperty("test".asName()).int) + assertEquals(22, box?.getProperty("test").int) } @Test @@ -37,7 +38,7 @@ class PropertyTest { } } } - assertEquals(22, box?.getProperty("test".asName()).int) + assertEquals(22, box?.getProperty("test").int) } @Test diff --git a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/MeshThreeFactory.kt b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/MeshThreeFactory.kt index 519fa9b3..8a80527d 100644 --- a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/MeshThreeFactory.kt +++ b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/MeshThreeFactory.kt @@ -85,7 +85,7 @@ internal fun Mesh.applyProperties(obj: Solid): Mesh = apply { } } -internal fun Mesh.applyEdges(obj: Solid) { +public fun Mesh.applyEdges(obj: Solid) { val edges = children.find { it.name == "@edges" } as? LineSegments //inherited edges definition, enabled by default if (obj.getProperty(MeshThreeFactory.EDGES_ENABLED_KEY).boolean != false) { @@ -111,7 +111,7 @@ internal fun Mesh.applyEdges(obj: Solid) { } } -internal fun Mesh.applyWireFrame(obj: Solid) { +public fun Mesh.applyWireFrame(obj: Solid) { children.find { it.name == "@wireframe" }?.let { remove(it) (it as LineSegments).dispose() diff --git a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeCanvasLabelFactory.kt b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeCanvasLabelFactory.kt index 90027451..f8d26cec 100644 --- a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeCanvasLabelFactory.kt +++ b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeCanvasLabelFactory.kt @@ -22,7 +22,7 @@ import kotlin.reflect.KClass public object ThreeCanvasLabelFactory : ThreeFactory { override val type: KClass get() = SolidLabel::class - override fun invoke(obj: SolidLabel): Object3D { + override fun invoke(three: ThreePlugin, obj: SolidLabel): Object3D { val canvas = document.createElement("canvas") as HTMLCanvasElement val context = canvas.getContext("2d") as CanvasRenderingContext2D context.font = "Bold ${obj.fontSize}pt ${obj.fontFamily}" diff --git a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreePlugin.kt b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreePlugin.kt index 9eacdea9..1bd46f37 100644 --- a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreePlugin.kt +++ b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreePlugin.kt @@ -25,7 +25,6 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer { private val objectFactories = HashMap, ThreeFactory<*>>() private val compositeFactory = ThreeCompositeFactory(this) - private val refFactory = ThreeReferenceFactory(this) //TODO generate a separate supervisor update scope internal val updateScope: CoroutineScope get() = context @@ -49,8 +48,8 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer { public fun buildObject3D(obj: Solid): Object3D { return when (obj) { - is ThreeVision -> obj.render() - is SolidReferenceGroup -> refFactory(obj) + is ThreeVision -> obj.render(this) + is SolidReferenceGroup -> ThreeReferenceFactory(this, obj) is SolidGroup -> { val group = ThreeGroup() obj.children.forEach { (token, child) -> @@ -108,13 +107,13 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer { }.launchIn(updateScope) } } - is Composite -> compositeFactory(obj) + is Composite -> compositeFactory(this, obj) else -> { //find specialized factory for this type if it is present val factory: ThreeFactory? = findObjectFactory(obj::class) when { - factory != null -> factory(obj) - obj is GeometrySolid -> ThreeShapeFactory(obj) + factory != null -> factory(this, obj) + obj is GeometrySolid -> ThreeShapeFactory(this, obj) else -> error("Renderer for ${obj::class} not found") } } diff --git a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeReferenceFactory.kt b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeReferenceFactory.kt index 02739b1c..2374c05f 100644 --- a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeReferenceFactory.kt +++ b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeReferenceFactory.kt @@ -13,7 +13,7 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlin.reflect.KClass -public class ThreeReferenceFactory(public val three: ThreePlugin) : ThreeFactory { +public object ThreeReferenceFactory : ThreeFactory { private val cache = HashMap() override val type: KClass = SolidReferenceGroup::class @@ -32,7 +32,7 @@ public class ThreeReferenceFactory(public val three: ThreePlugin) : ThreeFactory } } - override fun invoke(obj: SolidReferenceGroup): Object3D { + override fun invoke(three: ThreePlugin, obj: SolidReferenceGroup): Object3D { val template = obj.prototype val cachedObject = cache.getOrPut(template) { three.buildObject3D(template) diff --git a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeVision.kt b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeVision.kt index 67870229..8a6d2a52 100644 --- a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeVision.kt +++ b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeVision.kt @@ -7,5 +7,5 @@ import info.laht.threekt.core.Object3D * A custom visual object that has its own Three.js renderer */ public abstract class ThreeVision : SolidBase() { - public abstract fun render(): Object3D + public abstract fun render(three: ThreePlugin): Object3D } From 97cdfd3719e9001baf04d5fa22175da329dca0d8 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Thu, 17 Dec 2020 21:25:25 +0300 Subject: [PATCH 04/19] [WIP] Change property and structure subscription to flows. --- .../mipt/npm/muon/monitor/MMAppComponent.kt | 4 +- .../vision/solid/demo/VariableBox.kt | 7 +- settings.gradle.kts | 4 +- .../vision/bootstrap/visionPropertyEditor.kt | 19 +- .../{ConfigEditor.kt => PropertyEditor.kt} | 164 ++++++++++-------- .../kotlin/hep/dataforge/vision/StyleSheet.kt | 9 + .../kotlin/hep/dataforge/vision/Vision.kt | 79 +++------ .../kotlin/hep/dataforge/vision/VisionBase.kt | 31 ++-- .../hep/dataforge/vision/VisionChange.kt | 4 +- .../kotlin/hep/dataforge/vision/misc.kt | 17 +- .../hep/dataforge/vision/visionDescriptor.kt | 25 +++ .../editor/VisualObjectEditorFragment.kt | 4 +- .../vision/solid/FXReferenceFactory.kt | 2 +- .../vision/solid/VisualObjectFXBinding.kt | 2 +- .../dataforge/vision/gdml/GDMLTransformer.kt | 3 +- .../dataforge/vision/gdml/GdmlOptimizer.kt | 5 +- .../hep/dataforge/vision/solid/Composite.kt | 3 +- .../hep/dataforge/vision/solid/PolyLine.kt | 4 +- .../hep/dataforge/vision/solid/Solid.kt | 6 +- .../hep/dataforge/vision/solid/SolidGroup.kt | 2 +- .../dataforge/vision/solid/SolidMaterial.kt | 20 ++- .../dataforge/vision/solid/SolidReference.kt | 18 +- .../solid/transform/RemoveSingleChild.kt | 2 +- .../dataforge/vision/solid/PropertyTest.kt | 5 +- .../vision/solid/SerializationTest.kt | 6 +- .../vision/solid/VisionUpdateTest.kt | 2 +- .../vision/solid/three/MeshThreeFactory.kt | 2 +- .../vision/solid/three/ThreeLabelFactory.kt | 2 +- .../vision/solid/three/ThreeLineFactory.kt | 2 +- .../vision/solid/three/ThreePlugin.kt | 2 +- .../solid/three/ThreeReferenceFactory.kt | 2 +- 31 files changed, 250 insertions(+), 207 deletions(-) rename ui/react/src/main/kotlin/hep/dataforge/vision/react/{ConfigEditor.kt => PropertyEditor.kt} (60%) create mode 100644 visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/visionDescriptor.kt diff --git a/demo/muon-monitor/src/jsMain/kotlin/ru/mipt/npm/muon/monitor/MMAppComponent.kt b/demo/muon-monitor/src/jsMain/kotlin/ru/mipt/npm/muon/monitor/MMAppComponent.kt index 37220400..66a871cf 100644 --- a/demo/muon-monitor/src/jsMain/kotlin/ru/mipt/npm/muon/monitor/MMAppComponent.kt +++ b/demo/muon-monitor/src/jsMain/kotlin/ru/mipt/npm/muon/monitor/MMAppComponent.kt @@ -10,9 +10,9 @@ import hep.dataforge.vision.bootstrap.canvasControls import hep.dataforge.vision.bootstrap.card import hep.dataforge.vision.bootstrap.gridRow import hep.dataforge.vision.react.ThreeCanvasComponent -import hep.dataforge.vision.react.configEditor import hep.dataforge.vision.react.flexColumn import hep.dataforge.vision.react.objectTree +import hep.dataforge.vision.react.propertyEditor import hep.dataforge.vision.solid.specifications.Camera import hep.dataforge.vision.solid.specifications.Canvas3DOptions import hep.dataforge.vision.solid.three.ThreeCanvas @@ -193,7 +193,7 @@ val MMApp = functionalComponent("Muon monitor") { props -> else -> root[selected] } if (selectedObject != null) { - configEditor( + propertyEditor( selectedObject.config, selectedObject.descriptor, default = selectedObject.allProperties, diff --git a/demo/spatial-showcase/src/jsMain/kotlin/hep/dataforge/vision/solid/demo/VariableBox.kt b/demo/spatial-showcase/src/jsMain/kotlin/hep/dataforge/vision/solid/demo/VariableBox.kt index d4e8d833..366acfc4 100644 --- a/demo/spatial-showcase/src/jsMain/kotlin/hep/dataforge/vision/solid/demo/VariableBox.kt +++ b/demo/spatial-showcase/src/jsMain/kotlin/hep/dataforge/vision/solid/demo/VariableBox.kt @@ -5,7 +5,6 @@ import hep.dataforge.names.plus import hep.dataforge.names.startsWith import hep.dataforge.values.asValue import hep.dataforge.vision.getProperty -import hep.dataforge.vision.properties import hep.dataforge.vision.set import hep.dataforge.vision.setProperty import hep.dataforge.vision.solid.* @@ -34,8 +33,8 @@ internal class VariableBox(xSize: Number, ySize: Number, zSize: Number) : ThreeV scaleX = xSize scaleY = ySize scaleZ = zSize - properties[MeshThreeFactory.EDGES_ENABLED_KEY] = false - properties[MeshThreeFactory.WIREFRAME_ENABLED_KEY] = false + getProperty(MeshThreeFactory.EDGES_ENABLED_KEY, false) + getProperty(MeshThreeFactory.WIREFRAME_ENABLED_KEY, false) } override fun render(three: ThreePlugin): Object3D { @@ -63,7 +62,7 @@ internal class VariableBox(xSize: Number, ySize: Number, zSize: Number) : ThreeV mesh.scale.set(xSize, ySize, zSize) //add listener to object properties - propertyInvalidated.onEach { name -> + propertyNameFlow.onEach { name -> when { name.startsWith(GEOMETRY_KEY) -> { val newXSize = getProperty(X_SIZE_KEY, false).number?.toDouble() ?: 1.0 diff --git a/settings.gradle.kts b/settings.gradle.kts index 978abd5e..3877a947 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,6 +1,6 @@ pluginManagement { - val kotlinVersion = "1.4.20" - val toolsVersion = "0.7.0" + val kotlinVersion = "1.4.21" + val toolsVersion = "0.7.1" repositories { mavenLocal() diff --git a/ui/bootstrap/src/main/kotlin/hep/dataforge/vision/bootstrap/visionPropertyEditor.kt b/ui/bootstrap/src/main/kotlin/hep/dataforge/vision/bootstrap/visionPropertyEditor.kt index 9734bc74..f172f113 100644 --- a/ui/bootstrap/src/main/kotlin/hep/dataforge/vision/bootstrap/visionPropertyEditor.kt +++ b/ui/bootstrap/src/main/kotlin/hep/dataforge/vision/bootstrap/visionPropertyEditor.kt @@ -3,29 +3,30 @@ package hep.dataforge.vision.bootstrap import hep.dataforge.meta.Meta import hep.dataforge.meta.descriptors.NodeDescriptor import hep.dataforge.vision.Vision +import hep.dataforge.vision.allProperties import hep.dataforge.vision.getStyle -import hep.dataforge.vision.properties import hep.dataforge.vision.react.configEditor import hep.dataforge.vision.react.metaViewer +import hep.dataforge.vision.styles import org.w3c.dom.Element import react.RBuilder import react.dom.render public fun RBuilder.visionPropertyEditor( - item: Vision, - descriptor: NodeDescriptor? = item.descriptor, + vision: Vision, + descriptor: NodeDescriptor? = vision.descriptor, default: Meta? = null, - key: Any? = null + key: Any? = null, ) { card("Properties") { - configEditor(item.properties, descriptor, default, key) + configEditor(vision.allProperties(), descriptor, default, key) } - val styles = item.styles - if(styles.isNotEmpty()) { + val styles = vision.styles + if (styles.isNotEmpty()) { card("Styles") { accordion("styles") { styles.forEach { styleName -> - val style = item.getStyle(styleName) + val style = vision.getStyle(styleName) if (style != null) { entry(styleName) { metaViewer(style) @@ -40,7 +41,7 @@ public fun RBuilder.visionPropertyEditor( public fun Element.visionPropertyEditor( item: Vision, descriptor: NodeDescriptor? = item.descriptor, - default: Meta? = null + default: Meta? = null, ): Unit = render(this) { visionPropertyEditor(item, descriptor, default) } \ No newline at end of file diff --git a/ui/react/src/main/kotlin/hep/dataforge/vision/react/ConfigEditor.kt b/ui/react/src/main/kotlin/hep/dataforge/vision/react/PropertyEditor.kt similarity index 60% rename from ui/react/src/main/kotlin/hep/dataforge/vision/react/ConfigEditor.kt rename to ui/react/src/main/kotlin/hep/dataforge/vision/react/PropertyEditor.kt index b659452f..e88e42a1 100644 --- a/ui/react/src/main/kotlin/hep/dataforge/vision/react/ConfigEditor.kt +++ b/ui/react/src/main/kotlin/hep/dataforge/vision/react/PropertyEditor.kt @@ -7,6 +7,14 @@ import hep.dataforge.names.NameToken import hep.dataforge.names.lastOrNull import hep.dataforge.names.plus import hep.dataforge.values.Value +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch import kotlinx.html.js.onClickFunction import org.w3c.dom.Element import org.w3c.dom.events.Event @@ -14,55 +22,59 @@ import react.* import react.dom.render import styled.* -public external interface ConfigEditorItemProps : RProps { +public external interface PropertyEditorItemProps : RProps { /** * Root config object - always non null */ - public var root: MutableItemProvider + public var provider: MutableItemProvider /** - * Full path to the displayed node in [root]. Could be empty + * Full path to the displayed node in [provider]. Could be empty */ public var name: Name - /** - * Root default - */ - public var default: Meta? - /** * Root descriptor */ public var descriptor: NodeDescriptor? + + + public var scope: CoroutineScope? + + /** + * + */ + public var updateFlow: Flow? } -private val ConfigEditorItem: FunctionalComponent = +private val PropertyEditorItem: FunctionalComponent = functionalComponent("ConfigEditorItem") { props -> - configEditorItem(props) + propertyEditorItem(props) } -private fun RBuilder.configEditorItem(props: ConfigEditorItemProps) { +private fun RBuilder.propertyEditorItem(props: PropertyEditorItemProps) { var expanded: Boolean by useState { true } - var item: MetaItem<*>? by useState { props.root.getItem(props.name) } + var item: MetaItem<*>? by useState { props.provider.getItem(props.name) } val descriptorItem: ItemDescriptor? = props.descriptor?.get(props.name) - val defaultItem = props.default?.get(props.name) - var actualItem: MetaItem? by useState { item ?: defaultItem ?: descriptorItem?.defaultItem() } + var actualItem: MetaItem? by useState { item ?: descriptorItem?.defaultItem() } val token = props.name.lastOrNull()?.toString() ?: "Properties" fun update() { - item = props.root.getItem(props.name) - actualItem = item ?: defaultItem ?: descriptorItem?.defaultItem() + item = props.provider.getItem(props.name) + actualItem = item ?: descriptorItem?.defaultItem() } - useEffectWithCleanup(listOf(props.root)) { - props.root.onChange(this) { name, _, _ -> - if (name == props.name) { - update() - } + if (props.updateFlow != null) { + useEffectWithCleanup(listOf(props.provider, props.updateFlow)) { + val updateJob = props.updateFlow!!.onEach { updatedName -> + if (updatedName == props.name) { + update() + } + }.launchIn(props.scope ?: GlobalScope) + return@useEffectWithCleanup { updateJob.cancel() } } - return@useEffectWithCleanup { props.root.removeListener(this) } } val expanderClick: (Event) -> Unit = { @@ -71,15 +83,15 @@ private fun RBuilder.configEditorItem(props: ConfigEditorItemProps) { val valueChanged: (Value?) -> Unit = { if (it == null) { - props.root.remove(props.name) + props.provider.remove(props.name) } else { - props.root[props.name] = it + props.provider[props.name] = it } update() } val removeClick: (Event) -> Unit = { - props.root.remove(props.name) + props.provider.remove(props.name) update() } @@ -121,7 +133,6 @@ private fun RBuilder.configEditorItem(props: ConfigEditorItemProps) { add(NameToken(it)) } item?.node?.items?.keys?.let { addAll(it) } - defaultItem?.node?.items?.keys?.let { addAll(it) } } keys.filter { !it.body.startsWith("@") }.forEach { token -> @@ -129,12 +140,11 @@ private fun RBuilder.configEditorItem(props: ConfigEditorItemProps) { css { +TreeStyles.treeItem } - child(ConfigEditorItem) { + child(PropertyEditorItem) { attrs { this.key = props.name.toString() - this.root = props.root + this.provider = props.provider this.name = props.name + token - this.default = props.default this.descriptor = props.descriptor } } @@ -190,63 +200,79 @@ private fun RBuilder.configEditorItem(props: ConfigEditorItemProps) { } } -public external interface ConfigEditorProps : RProps { - public var id: Name - public var root: MutableItemProvider - public var default: Meta? +public external interface PropertyEditorProps : RProps { + public var provider: MutableItemProvider + public var updateFlow: Flow? public var descriptor: NodeDescriptor? + public var scope: CoroutineScope? } @JsExport -public val ConfigEditor: FunctionalComponent = functionalComponent("ConfigEditor") { props -> - child(ConfigEditorItem) { +public val PropertyEditor: FunctionalComponent = functionalComponent("ConfigEditor") { props -> + child(PropertyEditorItem) { attrs { this.key = "" - this.root = props.root + this.provider = props.provider this.name = Name.EMPTY - this.default = props.default this.descriptor = props.descriptor + this.scope = props.scope } } } +public fun RBuilder.propertyEditor( + provider: MutableItemProvider, + updateFlow: Flow? = null, + descriptor: NodeDescriptor? = null, + key: Any? = null, + scope: CoroutineScope? = null, +) { + child(PropertyEditor) { + attrs { + this.key = key?.toString() ?: "" + this.provider = provider + this.updateFlow = updateFlow + this.descriptor = descriptor + this.scope = scope + } + } +} + +private fun Config.flowUpdates(): Flow = callbackFlow { + onChange(this) { name, _, _ -> + launch { + send(name) + } + } + awaitClose { + removeListener(this) + } +} + +public fun MutableItemProvider.withDefault(default: ItemProvider): MutableItemProvider = object : MutableItemProvider { + override fun getItem(name: Name): MetaItem<*>? = getItem(name) ?: default.getItem(name) + + override fun setItem(name: Name, item: MetaItem<*>?) = this@withDefault.setItem(name, item) +} + + + +public fun RBuilder.configEditor( + config: Config, + descriptor: NodeDescriptor? = null, + default: Meta? = null, + key: Any? = null, + scope: CoroutineScope? = null, +) = propertyEditor(config.withDefault(default ?: ItemProvider.EMPTY), config.flowUpdates(), descriptor, key, scope) + public fun Element.configEditor( config: Config, descriptor: NodeDescriptor? = null, default: Meta? = null, key: Any? = null, + scope: CoroutineScope? = null, ) { render(this) { - child(ConfigEditor) { - attrs { - this.key = key?.toString() ?: "" - this.root = config - this.descriptor = descriptor - this.default = default - } - } + configEditor(config,descriptor,default, key, scope) } -} - -public fun RBuilder.configEditor( - config: MutableItemProvider, - descriptor: NodeDescriptor? = null, - default: Meta? = null, - key: Any? = null, -) { - child(ConfigEditor) { - attrs { - this.key = key?.toString() ?: "" - this.root = config - this.descriptor = descriptor - this.default = default - } - } -} -// -//public fun RBuilder.configEditor( -// obj: Configurable, -// descriptor: NodeDescriptor?, -// default: Meta? = null, -// key: Any? = null -//): Unit = configEditor(obj.config, descriptor, default, key) +} \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/StyleSheet.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/StyleSheet.kt index e7a6d971..783b376c 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/StyleSheet.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/StyleSheet.kt @@ -64,6 +64,15 @@ 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 + get() = getOwnProperty(Vision.STYLE_KEY)?.stringList ?: emptyList() + set(value) { + setProperty(Vision.STYLE_KEY, value) + } + /** * A stylesheet for this group and its descendants. Stylesheet is not applied directly, * but instead is just a repository for named configurations. diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt index 4baa5b8a..e1e73800 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt @@ -1,15 +1,15 @@ package hep.dataforge.vision -import hep.dataforge.meta.* +import hep.dataforge.meta.MetaItem +import hep.dataforge.meta.MutableItemProvider import hep.dataforge.meta.descriptors.Described import hep.dataforge.meta.descriptors.NodeDescriptor +import hep.dataforge.meta.descriptors.get import hep.dataforge.names.Name import hep.dataforge.names.asName import hep.dataforge.names.toName import hep.dataforge.provider.Type -import hep.dataforge.values.asValue import hep.dataforge.vision.Vision.Companion.TYPE -import hep.dataforge.vision.Vision.Companion.VISIBLE_KEY import kotlinx.coroutines.flow.Flow import kotlinx.serialization.Transient @@ -26,7 +26,8 @@ public interface Vision : Described { public var parent: VisionGroup? /** - * A fast accessor method to get own property (no inheritance or styles + * 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<*>? @@ -46,13 +47,13 @@ public interface Vision : Described { /** * Set the property value */ - public fun setProperty(name: Name, item: MetaItem<*>?) + public fun setProperty(name: Name, item: MetaItem<*>?, notify: Boolean = true) /** * 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. */ - public val propertyInvalidated: Flow + public val propertyNameFlow: Flow /** @@ -60,20 +61,6 @@ public interface Vision : Described { */ public fun notifyPropertyChanged(propertyName: Name): Unit - /** - * List of names of styles applied to this object. Order matters. Not inherited. - */ - public var styles: List - get() = getProperty( - STYLE_KEY, - inherit = false, - includeStyles = false, - includeDefaults = true - )?.stringList ?: emptyList() - set(value) { - setProperty(STYLE_KEY, value) - } - /** * Update this vision using external meta. Children are not updated. */ @@ -90,19 +77,27 @@ public interface Vision : Described { } /** - * Convenient accessor for all properties of a vision. Provided properties include styles and defaults, but do not inherit. + * 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 */ -public val Vision.properties: MutableItemProvider - get() = object : MutableItemProvider { - override fun getItem(name: Name): MetaItem<*>? = getProperty(name, - inherit = false, - includeStyles = true, - includeDefaults = true +public fun Vision.allProperties( + inherit: Boolean? = null, + includeStyles: Boolean = true, + includeDefaults: Boolean = true, +): MutableItemProvider = object : MutableItemProvider { + override fun getItem(name: Name): MetaItem<*>? { + val actualInherit = inherit ?: descriptor?.get(name)?.inherited ?: false + return getProperty( + name, + inherit = actualInherit, + includeStyles = includeStyles, + includeDefaults = includeDefaults ) - - override fun setItem(name: Name, item: MetaItem<*>?): Unit = setProperty(name, item) } + override fun setItem(name: Name, item: MetaItem<*>?): Unit = setProperty(name, item) +} + /** * Get [Vision] property using key as a String */ @@ -116,31 +111,13 @@ public fun Vision.getProperty( /** * A convenience method to pair [getProperty] */ -public fun Vision.setProperty(key: Name, value: Any?) { - properties[key] = value +public fun Vision.setProperty(key: Name, item: Any?) { + setProperty(key, MetaItem.of(item)) } /** * A convenience method to pair [getProperty] */ -public fun Vision.setProperty(key: String, value: Any?) { - properties[key] = value +public fun Vision.setProperty(key: String, item: Any?) { + setProperty(key.toName(), MetaItem.of(item)) } - -/** - * Control visibility of the element - */ -public var Vision.visible: Boolean? - get() = getProperty(VISIBLE_KEY).boolean - set(value) = setProperty(VISIBLE_KEY, value?.asValue()) - -public fun Vision.props(inherit: Boolean = true): MutableItemProvider = object : MutableItemProvider { - override fun getItem(name: Name): MetaItem<*>? { - return getProperty(name, inherit) - } - - override fun setItem(name: Name, item: MetaItem<*>?) { - setProperty(name, item) - } - -} \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt index aa1fcba2..c608e4c4 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt @@ -1,9 +1,12 @@ package hep.dataforge.vision -import hep.dataforge.meta.* +import hep.dataforge.meta.Config +import hep.dataforge.meta.MetaItem +import hep.dataforge.meta.MutableMeta import hep.dataforge.meta.descriptors.NodeDescriptor import hep.dataforge.meta.descriptors.defaultItem import hep.dataforge.meta.descriptors.get +import hep.dataforge.meta.update import hep.dataforge.names.Name import hep.dataforge.names.asName import hep.dataforge.values.ValueType @@ -30,34 +33,28 @@ public open class VisionBase : Vision { /** * Object own properties excluding styles and inheritance */ - @SerialName("properties") - protected var innerProperties: Config? = null + public var properties: Config? = null private set - /** - * All own properties as a read-only Meta - */ - public val ownProperties: Meta get() = innerProperties ?: Meta.EMPTY - @Synchronized private fun getOrCreateConfig(): Config { - if (innerProperties == null) { + if (properties == null) { val newProperties = Config() - innerProperties = newProperties + properties = newProperties newProperties.onChange(this) { name, oldItem, newItem -> if (oldItem != newItem) { notifyPropertyChanged(name) } } } - return innerProperties!! + return properties!! } /** * A fast accessor method to get own property (no inheritance or styles */ override fun getOwnProperty(name: Name): MetaItem<*>? { - return innerProperties?.getItem(name) + return properties?.getItem(name) } override fun getProperty( @@ -77,9 +74,11 @@ public open class VisionBase : Vision { }.merge() @Synchronized - override fun setProperty(name: Name, item: MetaItem<*>?) { + override fun setProperty(name: Name, item: MetaItem<*>?, notify: Boolean) { getOrCreateConfig().setItem(name, item) - notifyPropertyChanged(name) + if(notify) { + notifyPropertyChanged(name) + } } override val descriptor: NodeDescriptor? get() = null @@ -96,11 +95,11 @@ public open class VisionBase : Vision { @Transient private val _propertyInvalidationFlow: MutableSharedFlow = MutableSharedFlow() - override val propertyInvalidated: SharedFlow get() = _propertyInvalidationFlow + override val propertyNameFlow: SharedFlow get() = _propertyInvalidationFlow override fun notifyPropertyChanged(propertyName: Name) { if (propertyName == STYLE_KEY) { - updateStyles(properties.getItem(STYLE_KEY)?.stringList ?: emptyList()) + updateStyles(styles) } _propertyInvalidationFlow.tryEmit(propertyName) diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionChange.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionChange.kt index f2ac5d6b..c2717108 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionChange.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionChange.kt @@ -64,7 +64,7 @@ private fun Vision.isolate(manager: VisionManager): Vision { public class VisionChange( public val reset: Boolean = false, public val vision: Vision? = null, - public val properties: Meta? = null, + @Serializable(MetaSerializer::class) public val properties: Meta? = null, public val children: Map? = null, ) { @@ -81,7 +81,7 @@ private fun CoroutineScope.collectChange( ) { //Collect properties change - source.propertyInvalidated.onEach { propertyName -> + source.propertyNameFlow.onEach { propertyName -> val newItem = source.getProperty(propertyName, inherit = false, includeStyles = false, includeDefaults = false) collector().propertyChanged(name, propertyName, newItem) }.launchIn(this) diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/misc.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/misc.kt index 7c9a4521..75cb8f40 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/misc.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/misc.kt @@ -31,16 +31,15 @@ public inline fun > NodeDescriptor.enum(key: Name, default: } @DFExperimental -public val Vision.ownProperties: Meta? - get() = (this as? VisionBase)?.ownProperties +public val Vision.properties: Config? + get() = (this as? VisionBase)?.properties -@DFExperimental -public val Vision.describedProperties: Meta - get() = Meta { - descriptor?.items?.forEach { (key, _) -> - key put getProperty(key) - } - } +/** + * Control visibility of the element + */ +public var Vision.visible: Boolean? + get() = getProperty(Vision.VISIBLE_KEY).boolean + set(value) = setProperty(Vision.VISIBLE_KEY, value?.asValue()) public fun Vision.configure(meta: Meta?): Unit = update(VisionChange(properties = meta)) diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/visionDescriptor.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/visionDescriptor.kt new file mode 100644 index 00000000..3393c838 --- /dev/null +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/visionDescriptor.kt @@ -0,0 +1,25 @@ +package hep.dataforge.vision + +import hep.dataforge.meta.Meta +import hep.dataforge.meta.boolean +import hep.dataforge.meta.descriptors.ItemDescriptor +import hep.dataforge.meta.descriptors.attributes +import hep.dataforge.meta.get +import hep.dataforge.meta.set + +private const val INHERITED_DESCRIPTOR_ATTRIBUTE = "inherited" + +public var ItemDescriptor.inherited: Boolean + get() = attributes[INHERITED_DESCRIPTOR_ATTRIBUTE].boolean ?: false + set(value) = attributes { + set(INHERITED_DESCRIPTOR_ATTRIBUTE, value) + } + + +public val Vision.describedProperties: Meta + get() = Meta { + descriptor?.items?.forEach { (key, descriptor) -> + key put getProperty(key, inherit = descriptor.inherited) + } + } + diff --git a/visionforge-fx/src/main/kotlin/hep/dataforge/vision/editor/VisualObjectEditorFragment.kt b/visionforge-fx/src/main/kotlin/hep/dataforge/vision/editor/VisualObjectEditorFragment.kt index e8b22dbe..762c8090 100644 --- a/visionforge-fx/src/main/kotlin/hep/dataforge/vision/editor/VisualObjectEditorFragment.kt +++ b/visionforge-fx/src/main/kotlin/hep/dataforge/vision/editor/VisualObjectEditorFragment.kt @@ -19,8 +19,8 @@ class VisualObjectEditorFragment(val selector: (Vision) -> Meta) : Fragment() { constructor( item: Vision?, descriptor: NodeDescriptor?, - selector: (Vision) -> MutableItemProvider = { it.properties } - ) : this({it.describedProperties}) { + selector: (Vision) -> MutableItemProvider = { it.allProperties() }, + ) : this({ it.describedProperties }) { this.item = item this.descriptorProperty.set(descriptor) } diff --git a/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/FXReferenceFactory.kt b/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/FXReferenceFactory.kt index 2ffb2660..9b15df72 100644 --- a/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/FXReferenceFactory.kt +++ b/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/FXReferenceFactory.kt @@ -15,7 +15,7 @@ class FXReferenceFactory(val plugin: FX3DPlugin) : FX3DFactory + obj.propertyNameFlow.onEach { name-> if (name.firstOrNull()?.body == SolidReferenceGroup.REFERENCE_CHILD_PROPERTY_PREFIX) { val childName = name.firstOrNull()?.index?.toName() ?: error("Wrong syntax for reference child property: '$name'") val propertyName = name.cutFirst() diff --git a/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/VisualObjectFXBinding.kt b/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/VisualObjectFXBinding.kt index 8d19db59..30a8d6a8 100644 --- a/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/VisualObjectFXBinding.kt +++ b/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/VisualObjectFXBinding.kt @@ -18,7 +18,7 @@ class VisualObjectFXBinding(val fx: FX3DPlugin, val obj: Vision) { private val bindings = HashMap?>>() init { - obj.propertyInvalidated.onEach { name -> + obj.propertyNameFlow.onEach { name -> bindings.filter { it.key.startsWith(name) }.forEach { entry -> Platform.runLater { entry.value.invalidate() diff --git a/visionforge-gdml/src/commonMain/kotlin/hep/dataforge/vision/gdml/GDMLTransformer.kt b/visionforge-gdml/src/commonMain/kotlin/hep/dataforge/vision/gdml/GDMLTransformer.kt index 58ec3b51..2ac28d6b 100644 --- a/visionforge-gdml/src/commonMain/kotlin/hep/dataforge/vision/gdml/GDMLTransformer.kt +++ b/visionforge-gdml/src/commonMain/kotlin/hep/dataforge/vision/gdml/GDMLTransformer.kt @@ -38,7 +38,6 @@ public class GDMLTransformerSettings { public var solidAction: (GDMLSolid) -> Action = { Action.PROTOTYPE } public var volumeAction: (GDMLGroup) -> Action = { Action.PROTOTYPE } - } private class GDMLTransformer(val settings: GDMLTransformerSettings) { @@ -324,7 +323,7 @@ private class GDMLTransformer(val settings: GDMLTransformerSettings) { } } - fun finalize(final: SolidGroup): SolidGroup { + private fun finalize(final: SolidGroup): SolidGroup { //final.prototypes = proto final.useStyle("GDML") { Solid.ROTATION_ORDER_KEY put RotationOrder.ZXY diff --git a/visionforge-gdml/src/commonMain/kotlin/hep/dataforge/vision/gdml/GdmlOptimizer.kt b/visionforge-gdml/src/commonMain/kotlin/hep/dataforge/vision/gdml/GdmlOptimizer.kt index bec3822c..125614c6 100644 --- a/visionforge-gdml/src/commonMain/kotlin/hep/dataforge/vision/gdml/GdmlOptimizer.kt +++ b/visionforge-gdml/src/commonMain/kotlin/hep/dataforge/vision/gdml/GdmlOptimizer.kt @@ -3,7 +3,6 @@ package hep.dataforge.vision.gdml import hep.dataforge.meta.DFExperimental import hep.dataforge.meta.sequence import hep.dataforge.vision.Vision -import hep.dataforge.vision.ownProperties import hep.dataforge.vision.properties import hep.dataforge.vision.solid.* @@ -28,8 +27,8 @@ internal fun Vision.updateFrom(other: Vision): Vision { scaleY = scaleY.toDouble() * other.scaleY.toDouble() scaleZ = scaleZ.toDouble() * other.scaleZ.toDouble() } - other.ownProperties?.sequence()?.forEach { (name, item) -> - if (properties.getItem(name) == null) { + other.properties?.sequence()?.forEach { (name, item) -> + if (getProperty(name) == null) { setProperty(name, item) } } diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Composite.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Composite.kt index dacc79bd..e1e79ef8 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Composite.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Composite.kt @@ -1,5 +1,6 @@ package hep.dataforge.vision.solid +import hep.dataforge.meta.Meta import hep.dataforge.meta.update import hep.dataforge.names.NameToken import hep.dataforge.vision.* @@ -39,7 +40,7 @@ public inline fun VisionContainerBuilder.composite( val children = group.children.values.filterIsInstance() if (children.size != 2) error("Composite requires exactly two children") return Composite(type, children[0], children[1]).also { - it.configure { update(group.ownProperties) } + it.configure { update(group.properties ?: Meta.EMPTY) } if (group.position != null) { it.position = group.position } diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/PolyLine.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/PolyLine.kt index 998f7f4f..28f4ad68 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/PolyLine.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/PolyLine.kt @@ -6,7 +6,7 @@ import hep.dataforge.names.asName import hep.dataforge.names.plus import hep.dataforge.vision.VisionBuilder import hep.dataforge.vision.VisionContainerBuilder -import hep.dataforge.vision.props +import hep.dataforge.vision.allProperties import hep.dataforge.vision.set import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -16,7 +16,7 @@ import kotlinx.serialization.Serializable public class PolyLine(public var points: List) : SolidBase(), Solid { //var lineType by string() - public var thickness: Number by props().number(1.0, key = SolidMaterial.MATERIAL_KEY + THICKNESS_KEY) + public var thickness: Number by allProperties(inherit = false).number(1.0, key = SolidMaterial.MATERIAL_KEY + THICKNESS_KEY) public companion object { public val THICKNESS_KEY: Name = "thickness".asName() diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Solid.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Solid.kt index d690c6b1..65cf37e4 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Solid.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Solid.kt @@ -81,7 +81,7 @@ public interface Solid : Vision { if (first.position != second.position) return false if (first.rotation != second.rotation) return false if (first.scale != second.scale) return false - if (first.ownProperties != second.ownProperties) return false + if (first.properties != second.properties) return false return true } @@ -89,7 +89,7 @@ public interface Solid : Vision { var result = +(solid.position?.hashCode() ?: 0) result = 31 * result + (solid.rotation?.hashCode() ?: 0) result = 31 * result + (solid.scale?.hashCode() ?: 0) - result = 31 * result + solid.properties.hashCode() + result = 31 * result + solid.allProperties().hashCode() return result } } @@ -99,7 +99,7 @@ public interface Solid : Vision { * Get the layer number this solid belongs to. Return 0 if layer is not defined. */ public var Solid.layer: Int - get() = properties.getItem(LAYER_KEY).int ?: 0 + get() = allProperties().getItem(LAYER_KEY).int ?: 0 set(value) { setProperty(LAYER_KEY, value) } diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt index cc3b92e4..77aa950c 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt @@ -130,7 +130,7 @@ internal class Prototypes( includeDefaults: Boolean, ): MetaItem<*>? = null - override fun setProperty(name: Name, item: MetaItem<*>?) { + override fun setProperty(name: Name, item: MetaItem<*>?, notify: Boolean) { TODO("Not yet implemented") } diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidMaterial.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidMaterial.kt index 8cf8755c..f060afea 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidMaterial.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidMaterial.kt @@ -41,14 +41,14 @@ public operator fun ColorAccessor?.invoke(webColor: String) { * Set color as RGB integer */ public operator fun ColorAccessor?.invoke(rgb: Int) { - this?.value = rgb.asValue() + this?.value = Colors.rgbToString(rgb).asValue() } /** * Set color as RGB */ public operator fun ColorAccessor?.invoke(r: UByte, g: UByte, b: UByte) { - this?.value = Colors.rgbToString(r, g, b).asValue() + this?.value = Colors.rgbToString(r, g, b).asValue() } @VisionBuilder @@ -112,7 +112,15 @@ public class SolidMaterial : Scheme() { } } -public val Solid.color: ColorAccessor get() = ColorAccessor(properties, MATERIAL_COLOR_KEY) +public val Solid.color: ColorAccessor + get() = ColorAccessor( + allProperties( + inherit = true, + includeStyles = true, + includeDefaults = true + ), + MATERIAL_COLOR_KEY + ) public var Solid.material: SolidMaterial? get() = getProperty(MATERIAL_KEY).node?.let { SolidMaterial.read(it) } @@ -120,7 +128,11 @@ public var Solid.material: SolidMaterial? @VisionBuilder public fun Solid.material(builder: SolidMaterial.() -> Unit) { - val node = properties.getItem(MATERIAL_KEY).node + val node = allProperties( + inherit = true, + includeStyles = true, + includeDefaults = true + ).getItem(MATERIAL_KEY).node if (node != null) { SolidMaterial.update(node, builder) } else { diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidReference.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidReference.kt index efc7b1cb..5378f71d 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidReference.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidReference.kt @@ -47,12 +47,12 @@ public class SolidReferenceGroup( return getOwnProperty(childPropertyName(childName, propertyName)) } - private fun setChildProperty(childName: Name, propertyName: Name, item: MetaItem<*>?) { - setProperty(childPropertyName(childName, propertyName), item) + private fun setChildProperty(childName: Name, propertyName: Name, item: MetaItem<*>?, notify: Boolean) { + setProperty(childPropertyName(childName, propertyName), item, notify) } private fun prototypeFor(name: Name): Solid { - return if(name.isEmpty()) prototype else { + return if (name.isEmpty()) prototype else { (prototype as? SolidGroup)?.get(name) as? Solid ?: error("Prototype with name $name not found in $this") } @@ -98,8 +98,8 @@ public class SolidReferenceGroup( override fun getOwnProperty(name: Name): MetaItem<*>? = getChildProperty(childName, name) - override fun setProperty(name: Name, item: MetaItem<*>?) { - setChildProperty(childName, name, item) + override fun setProperty(name: Name, item: MetaItem<*>?, notify: Boolean) { + setChildProperty(childName, name, item, notify) } override fun getProperty( @@ -119,16 +119,16 @@ public class SolidReferenceGroup( }.merge() override var parent: VisionGroup? - get(){ + get() { val parentName = childName.cutLast() - return if( parentName.isEmpty()) this@SolidReferenceGroup else ReferenceChild(parentName) + return if (parentName.isEmpty()) this@SolidReferenceGroup else ReferenceChild(parentName) } set(value) { error("Setting a parent for a reference child is not possible") } - override val propertyInvalidated: Flow - get() = this@SolidReferenceGroup.propertyInvalidated.filter { name -> + override val propertyNameFlow: Flow + get() = this@SolidReferenceGroup.propertyNameFlow.filter { name -> name.startsWith(childToken(childName)) }.map { name -> name.cutFirst() diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/transform/RemoveSingleChild.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/transform/RemoveSingleChild.kt index 0b64a7b7..a5b5f1ba 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/transform/RemoveSingleChild.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/transform/RemoveSingleChild.kt @@ -9,7 +9,7 @@ import hep.dataforge.vision.solid.* internal fun mergeChild(parent: VisionGroup, child: Vision): Vision { return child.apply { - configure(parent.ownProperties) + configure(parent.properties) //parent.properties?.let { config.update(it) } diff --git a/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/PropertyTest.kt b/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/PropertyTest.kt index 55efe64a..30917b90 100644 --- a/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/PropertyTest.kt +++ b/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/PropertyTest.kt @@ -2,10 +2,7 @@ package hep.dataforge.vision.solid import hep.dataforge.meta.int import hep.dataforge.names.asName -import hep.dataforge.vision.getProperty -import hep.dataforge.vision.setProperty -import hep.dataforge.vision.styleSheet -import hep.dataforge.vision.useStyle +import hep.dataforge.vision.* import kotlin.test.Test import kotlin.test.assertEquals diff --git a/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/SerializationTest.kt b/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/SerializationTest.kt index 017d4db3..c7090eee 100644 --- a/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/SerializationTest.kt +++ b/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/SerializationTest.kt @@ -4,7 +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.ownProperties +import hep.dataforge.vision.properties import kotlin.test.Test import kotlin.test.assertEquals @@ -33,7 +33,7 @@ class SerializationTest { val string = SolidManager.encodeToString(cube) println(string) val newCube = SolidManager.decodeFromString(string) - assertEquals(cube.ownProperties, newCube.ownProperties) + assertEquals(cube.properties, newCube.properties) } @Test @@ -54,7 +54,7 @@ class SerializationTest { val string = SolidManager.encodeToString(group) println(string) val reconstructed = SolidManager.decodeFromString(string) as SolidGroup - assertEquals(group["cube"]?.ownProperties, reconstructed["cube"]?.ownProperties) + assertEquals(group["cube"]?.properties, reconstructed["cube"]?.properties) } } \ No newline at end of file diff --git a/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/VisionUpdateTest.kt b/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/VisionUpdateTest.kt index 79bba867..17babd41 100644 --- a/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/VisionUpdateTest.kt +++ b/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/VisionUpdateTest.kt @@ -29,7 +29,7 @@ class VisionUpdateTest { targetVision.update(dif) assertTrue { targetVision["top"] is SolidGroup } assertEquals("red", (targetVision["origin"] as Solid).color.string) // Should work - assertEquals("#00007b", (targetVision["top"] as SolidGroup).color.string) // new item always takes precedence + assertEquals("#00007b", (targetVision["top"] as Solid).color.string) // new item always takes precedence } @Test diff --git a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/MeshThreeFactory.kt b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/MeshThreeFactory.kt index 8a80527d..3adaf603 100644 --- a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/MeshThreeFactory.kt +++ b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/MeshThreeFactory.kt @@ -45,7 +45,7 @@ public abstract class MeshThreeFactory( }.applyProperties(obj) //add listener to object properties - obj.propertyInvalidated.onEach { name -> + obj.propertyNameFlow.onEach { name -> when { name.startsWith(Solid.GEOMETRY_KEY) -> { val oldGeometry = mesh.geometry as BufferGeometry diff --git a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeLabelFactory.kt b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeLabelFactory.kt index 9ab3f401..4d1c7d99 100644 --- a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeLabelFactory.kt +++ b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeLabelFactory.kt @@ -27,7 +27,7 @@ public object ThreeLabelFactory : ThreeFactory { }) return Mesh(textGeo, getMaterial(obj, true)).apply { updatePosition(obj) - obj.propertyInvalidated.onEach { _ -> + obj.propertyNameFlow.onEach { _ -> //TODO three.logger.warn{"Label parameter change not implemented"} }.launchIn(three.updateScope) diff --git a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeLineFactory.kt b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeLineFactory.kt index 199213ba..d914a218 100644 --- a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeLineFactory.kt +++ b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeLineFactory.kt @@ -30,7 +30,7 @@ public object ThreeLineFactory : ThreeFactory { updatePosition(obj) //layers.enable(obj.layer) //add listener to object properties - obj.propertyInvalidated.onEach { propertyName -> + obj.propertyNameFlow.onEach { propertyName -> updateProperty(obj, propertyName) }.launchIn(three.updateScope) } diff --git a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreePlugin.kt b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreePlugin.kt index 1bd46f37..2732d624 100644 --- a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreePlugin.kt +++ b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreePlugin.kt @@ -68,7 +68,7 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer { updatePosition(obj) //obj.onChildrenChange() - obj.propertyInvalidated.onEach { name -> + obj.propertyNameFlow.onEach { name -> if ( name.startsWith(Solid.POSITION_KEY) || name.startsWith(Solid.ROTATION) || diff --git a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeReferenceFactory.kt b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeReferenceFactory.kt index 2374c05f..08866d46 100644 --- a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeReferenceFactory.kt +++ b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeReferenceFactory.kt @@ -47,7 +47,7 @@ public object ThreeReferenceFactory : ThreeFactory { //TODO apply child properties - obj.propertyInvalidated.onEach { name-> + obj.propertyNameFlow.onEach { name-> if (name.firstOrNull()?.body == REFERENCE_CHILD_PROPERTY_PREFIX) { val childName = name.firstOrNull()?.index?.toName() ?: error("Wrong syntax for reference child property: '$name'") val propertyName = name.cutFirst() From be1d79c229b39b9f2c7f16d32423142895fc71c2 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 19 Dec 2020 12:33:29 +0300 Subject: [PATCH 05/19] Fix all compilations and errors. --- .../mipt/npm/muon/monitor/MMAppComponent.kt | 27 ++++++++----------- .../vision/bootstrap/threeControls.kt | 9 ++----- .../vision/bootstrap/visionPropertyEditor.kt | 9 ++++--- .../kotlin/hep/dataforge/vision/Vision.kt | 5 ++-- .../hep/dataforge/vision/visionDescriptor.kt | 7 +++++ .../hep/dataforge/vision/html/HtmlTagTest.kt | 2 +- 6 files changed, 30 insertions(+), 29 deletions(-) diff --git a/demo/muon-monitor/src/jsMain/kotlin/ru/mipt/npm/muon/monitor/MMAppComponent.kt b/demo/muon-monitor/src/jsMain/kotlin/ru/mipt/npm/muon/monitor/MMAppComponent.kt index 66a871cf..7e146287 100644 --- a/demo/muon-monitor/src/jsMain/kotlin/ru/mipt/npm/muon/monitor/MMAppComponent.kt +++ b/demo/muon-monitor/src/jsMain/kotlin/ru/mipt/npm/muon/monitor/MMAppComponent.kt @@ -9,10 +9,10 @@ import hep.dataforge.vision.Vision import hep.dataforge.vision.bootstrap.canvasControls import hep.dataforge.vision.bootstrap.card import hep.dataforge.vision.bootstrap.gridRow +import hep.dataforge.vision.bootstrap.visionPropertyEditor import hep.dataforge.vision.react.ThreeCanvasComponent import hep.dataforge.vision.react.flexColumn import hep.dataforge.vision.react.objectTree -import hep.dataforge.vision.react.propertyEditor import hep.dataforge.vision.solid.specifications.Camera import hep.dataforge.vision.solid.specifications.Canvas3DOptions import hep.dataforge.vision.solid.three.ThreeCanvas @@ -57,7 +57,7 @@ val MMApp = functionalComponent("Muon monitor") { props -> val root = props.model.root - gridRow{ + gridRow { flexColumn { css { +"col-lg-3" @@ -109,8 +109,8 @@ val MMApp = functionalComponent("Muon monitor") { props -> height = 100.vh } styledDiv { - css{ - flex(0.0,1.0, FlexBasis.zero) + css { + flex(0.0, 1.0, FlexBasis.zero) } //settings canvas?.let { @@ -140,8 +140,8 @@ val MMApp = functionalComponent("Muon monitor") { props -> } } } - styledDiv{ - css{ + styledDiv { + css { padding(0.px) } nav { @@ -180,10 +180,10 @@ val MMApp = functionalComponent("Muon monitor") { props -> } } } - styledDiv{ - css{ - overflowY = Overflow.auto - } + styledDiv { + css { + overflowY = Overflow.auto + } //properties card("Properties") { selected.let { selected -> @@ -193,12 +193,7 @@ val MMApp = functionalComponent("Muon monitor") { props -> else -> root[selected] } if (selectedObject != null) { - propertyEditor( - selectedObject.config, - selectedObject.descriptor, - default = selectedObject.allProperties, - key = selected - ) + visionPropertyEditor(selectedObject, key = selected) } } } diff --git a/ui/bootstrap/src/main/kotlin/hep/dataforge/vision/bootstrap/threeControls.kt b/ui/bootstrap/src/main/kotlin/hep/dataforge/vision/bootstrap/threeControls.kt index ea835f4f..7c26abbf 100644 --- a/ui/bootstrap/src/main/kotlin/hep/dataforge/vision/bootstrap/threeControls.kt +++ b/ui/bootstrap/src/main/kotlin/hep/dataforge/vision/bootstrap/threeControls.kt @@ -4,7 +4,6 @@ import hep.dataforge.names.Name import hep.dataforge.names.isEmpty import hep.dataforge.vision.Vision import hep.dataforge.vision.VisionGroup -import hep.dataforge.vision.describedProperties import hep.dataforge.vision.react.objectTree import hep.dataforge.vision.solid.three.ThreeCanvas import kotlinx.css.* @@ -52,11 +51,7 @@ public val ThreeControls: FunctionalComponent = functionalCo else -> (vision as? VisionGroup)?.get(selected) } if (selectedObject != null) { - visionPropertyEditor( - selectedObject, - default = selectedObject.describedProperties, - key = selected - ) + visionPropertyEditor(selectedObject, key = selected) } } } @@ -70,7 +65,7 @@ public fun RBuilder.threeControls( canvas: ThreeCanvas, selected: Name?, onSelect: (Name) -> Unit = {}, - builder: TabBuilder.() -> Unit = {} + builder: TabBuilder.() -> Unit = {}, ): ReactElement = child(ThreeControls) { attrs { this.canvas = canvas diff --git a/ui/bootstrap/src/main/kotlin/hep/dataforge/vision/bootstrap/visionPropertyEditor.kt b/ui/bootstrap/src/main/kotlin/hep/dataforge/vision/bootstrap/visionPropertyEditor.kt index f172f113..9f4c3680 100644 --- a/ui/bootstrap/src/main/kotlin/hep/dataforge/vision/bootstrap/visionPropertyEditor.kt +++ b/ui/bootstrap/src/main/kotlin/hep/dataforge/vision/bootstrap/visionPropertyEditor.kt @@ -5,8 +5,8 @@ import hep.dataforge.meta.descriptors.NodeDescriptor import hep.dataforge.vision.Vision import hep.dataforge.vision.allProperties import hep.dataforge.vision.getStyle -import hep.dataforge.vision.react.configEditor import hep.dataforge.vision.react.metaViewer +import hep.dataforge.vision.react.propertyEditor import hep.dataforge.vision.styles import org.w3c.dom.Element import react.RBuilder @@ -15,11 +15,14 @@ import react.dom.render public fun RBuilder.visionPropertyEditor( vision: Vision, descriptor: NodeDescriptor? = vision.descriptor, - default: Meta? = null, key: Any? = null, ) { card("Properties") { - configEditor(vision.allProperties(), descriptor, default, key) + propertyEditor( + vision.allProperties(), + updateFlow = vision.propertyNameFlow, + descriptor = descriptor, + key = key) } val styles = vision.styles if (styles.isNotEmpty()) { diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt index e1e73800..7012698d 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt @@ -82,15 +82,16 @@ public interface Vision : Described { */ public fun Vision.allProperties( inherit: Boolean? = null, - includeStyles: Boolean = true, + includeStyles: Boolean? = null, includeDefaults: Boolean = true, ): MutableItemProvider = object : MutableItemProvider { override fun getItem(name: Name): MetaItem<*>? { val actualInherit = inherit ?: descriptor?.get(name)?.inherited ?: false + val actualUseStyles = includeStyles ?: descriptor?.get(name)?.usesStyles ?: true return getProperty( name, inherit = actualInherit, - includeStyles = includeStyles, + includeStyles = actualUseStyles, includeDefaults = includeDefaults ) } diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/visionDescriptor.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/visionDescriptor.kt index 3393c838..0e068827 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/visionDescriptor.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/visionDescriptor.kt @@ -8,6 +8,7 @@ import hep.dataforge.meta.get import hep.dataforge.meta.set private const val INHERITED_DESCRIPTOR_ATTRIBUTE = "inherited" +private const val STYLE_DESCRIPTOR_ATTRIBUTE = "useStyles" public var ItemDescriptor.inherited: Boolean get() = attributes[INHERITED_DESCRIPTOR_ATTRIBUTE].boolean ?: false @@ -15,6 +16,12 @@ public var ItemDescriptor.inherited: Boolean set(INHERITED_DESCRIPTOR_ATTRIBUTE, value) } +public var ItemDescriptor.usesStyles: Boolean + get() = attributes[STYLE_DESCRIPTOR_ATTRIBUTE].boolean ?: true + set(value) = attributes { + set(STYLE_DESCRIPTOR_ATTRIBUTE, value) + } + public val Vision.describedProperties: Meta get() = Meta { diff --git a/visionforge-core/src/commonTest/kotlin/hep/dataforge/vision/html/HtmlTagTest.kt b/visionforge-core/src/commonTest/kotlin/hep/dataforge/vision/html/HtmlTagTest.kt index 0937bd64..c6efe090 100644 --- a/visionforge-core/src/commonTest/kotlin/hep/dataforge/vision/html/HtmlTagTest.kt +++ b/visionforge-core/src/commonTest/kotlin/hep/dataforge/vision/html/HtmlTagTest.kt @@ -34,7 +34,7 @@ class HtmlTagTest { div { h2 { +"Properties" } ul { - (vision as? VisionBase)?.ownProperties?.items?.forEach { + (vision as? VisionBase)?.properties?.items?.forEach { li { a { +it.key.toString() } p { +it.value.toString() } From 6939fba2926b11f34a2ed5302b0901504f58e4c7 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 19 Dec 2020 15:59:41 +0300 Subject: [PATCH 06/19] Fix getProperty defaults --- .../vision/bootstrap/visionPropertyEditor.kt | 12 +-- .../dataforge/vision/react/PropertyEditor.kt | 73 +++++++++---------- .../kotlin/hep/dataforge/vision/Vision.kt | 39 +++++----- .../kotlin/hep/dataforge/vision/VisionBase.kt | 8 +- .../hep/dataforge/vision/solid/SolidGroup.kt | 4 +- .../dataforge/vision/solid/SolidMaterial.kt | 10 +-- .../dataforge/vision/solid/SolidReference.kt | 49 +++++++------ .../dataforge/vision/solid/PropertyTest.kt | 2 +- 8 files changed, 97 insertions(+), 100 deletions(-) diff --git a/ui/bootstrap/src/main/kotlin/hep/dataforge/vision/bootstrap/visionPropertyEditor.kt b/ui/bootstrap/src/main/kotlin/hep/dataforge/vision/bootstrap/visionPropertyEditor.kt index 9f4c3680..14a93eb8 100644 --- a/ui/bootstrap/src/main/kotlin/hep/dataforge/vision/bootstrap/visionPropertyEditor.kt +++ b/ui/bootstrap/src/main/kotlin/hep/dataforge/vision/bootstrap/visionPropertyEditor.kt @@ -1,13 +1,9 @@ package hep.dataforge.vision.bootstrap -import hep.dataforge.meta.Meta import hep.dataforge.meta.descriptors.NodeDescriptor -import hep.dataforge.vision.Vision -import hep.dataforge.vision.allProperties -import hep.dataforge.vision.getStyle +import hep.dataforge.vision.* import hep.dataforge.vision.react.metaViewer import hep.dataforge.vision.react.propertyEditor -import hep.dataforge.vision.styles import org.w3c.dom.Element import react.RBuilder import react.dom.render @@ -19,7 +15,8 @@ public fun RBuilder.visionPropertyEditor( ) { card("Properties") { propertyEditor( - vision.allProperties(), + provider = vision.ownProperties, + defaultProvider = vision.allProperties(), updateFlow = vision.propertyNameFlow, descriptor = descriptor, key = key) @@ -44,7 +41,6 @@ public fun RBuilder.visionPropertyEditor( public fun Element.visionPropertyEditor( item: Vision, descriptor: NodeDescriptor? = item.descriptor, - default: Meta? = null, ): Unit = render(this) { - visionPropertyEditor(item, descriptor, default) + visionPropertyEditor(item, descriptor = descriptor) } \ No newline at end of file diff --git a/ui/react/src/main/kotlin/hep/dataforge/vision/react/PropertyEditor.kt b/ui/react/src/main/kotlin/hep/dataforge/vision/react/PropertyEditor.kt index e88e42a1..c730123b 100644 --- a/ui/react/src/main/kotlin/hep/dataforge/vision/react/PropertyEditor.kt +++ b/ui/react/src/main/kotlin/hep/dataforge/vision/react/PropertyEditor.kt @@ -22,17 +22,22 @@ import react.* import react.dom.render import styled.* -public external interface PropertyEditorItemProps : RProps { +public external interface PropertyEditorProps : RProps { /** * Root config object - always non null */ public var provider: MutableItemProvider + /** + * Provide default item (greyed out if used + */ + public var defaultProvider: ItemProvider? + /** * Full path to the displayed node in [provider]. Could be empty */ - public var name: Name + public var name: Name? /** * Root descriptor @@ -40,29 +45,35 @@ public external interface PropertyEditorItemProps : RProps { public var descriptor: NodeDescriptor? + /** + * A coroutine scope for updates + */ public var scope: CoroutineScope? /** - * + * Flow names of updated properties */ public var updateFlow: Flow? } -private val PropertyEditorItem: FunctionalComponent = +private val PropertyEditorItem: FunctionalComponent = functionalComponent("ConfigEditorItem") { props -> propertyEditorItem(props) } -private fun RBuilder.propertyEditorItem(props: PropertyEditorItemProps) { +private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) { var expanded: Boolean by useState { true } - var item: MetaItem<*>? by useState { props.provider.getItem(props.name) } - val descriptorItem: ItemDescriptor? = props.descriptor?.get(props.name) - var actualItem: MetaItem? by useState { item ?: descriptorItem?.defaultItem() } + val itemName by useState { props.name ?: Name.EMPTY } + var item: MetaItem<*>? by useState { props.provider.getItem(itemName) } + val descriptorItem: ItemDescriptor? = props.descriptor?.get(itemName) + var actualItem: MetaItem? by useState { + item ?: props.defaultProvider?.getItem(itemName) ?: descriptorItem?.defaultItem() + } - val token = props.name.lastOrNull()?.toString() ?: "Properties" + val token = itemName.lastOrNull()?.toString() ?: "Properties" fun update() { - item = props.provider.getItem(props.name) + item = props.provider.getItem(itemName) actualItem = item ?: descriptorItem?.defaultItem() } @@ -83,15 +94,15 @@ private fun RBuilder.propertyEditorItem(props: PropertyEditorItemProps) { val valueChanged: (Value?) -> Unit = { if (it == null) { - props.provider.remove(props.name) + props.provider.remove(itemName) } else { - props.provider[props.name] = it + props.provider[itemName] = it } update() } val removeClick: (Event) -> Unit = { - props.provider.remove(props.name) + props.provider.remove(itemName) update() } @@ -144,7 +155,7 @@ private fun RBuilder.propertyEditorItem(props: PropertyEditorItemProps) { attrs { this.key = props.name.toString() this.provider = props.provider - this.name = props.name + token + this.name = itemName + token this.descriptor = props.descriptor } } @@ -176,7 +187,7 @@ private fun RBuilder.propertyEditorItem(props: PropertyEditorItemProps) { +TreeStyles.resizeableInput } valueChooser( - props.name, + itemName, actualItem, descriptorItem as? ValueDescriptor, valueChanged @@ -200,19 +211,14 @@ private fun RBuilder.propertyEditorItem(props: PropertyEditorItemProps) { } } -public external interface PropertyEditorProps : RProps { - public var provider: MutableItemProvider - public var updateFlow: Flow? - public var descriptor: NodeDescriptor? - public var scope: CoroutineScope? -} @JsExport -public val PropertyEditor: FunctionalComponent = functionalComponent("ConfigEditor") { props -> +public val PropertyEditor: FunctionalComponent = functionalComponent("PropertyEditor") { props -> child(PropertyEditorItem) { attrs { this.key = "" this.provider = props.provider + this.defaultProvider = props.defaultProvider this.name = Name.EMPTY this.descriptor = props.descriptor this.scope = props.scope @@ -222,17 +228,19 @@ public val PropertyEditor: FunctionalComponent = functional public fun RBuilder.propertyEditor( provider: MutableItemProvider, + defaultProvider: ItemProvider?, updateFlow: Flow? = null, descriptor: NodeDescriptor? = null, - key: Any? = null, scope: CoroutineScope? = null, + key: Any? = null, ) { child(PropertyEditor) { attrs { - this.key = key?.toString() ?: "" this.provider = provider + this.defaultProvider = defaultProvider this.updateFlow = updateFlow this.descriptor = descriptor + this.key = key?.toString() ?: "" this.scope = scope } } @@ -249,21 +257,14 @@ private fun Config.flowUpdates(): Flow = callbackFlow { } } -public fun MutableItemProvider.withDefault(default: ItemProvider): MutableItemProvider = object : MutableItemProvider { - override fun getItem(name: Name): MetaItem<*>? = getItem(name) ?: default.getItem(name) - - override fun setItem(name: Name, item: MetaItem<*>?) = this@withDefault.setItem(name, item) -} - - public fun RBuilder.configEditor( config: Config, + default: ItemProvider? = null, descriptor: NodeDescriptor? = null, - default: Meta? = null, key: Any? = null, scope: CoroutineScope? = null, -) = propertyEditor(config.withDefault(default ?: ItemProvider.EMPTY), config.flowUpdates(), descriptor, key, scope) +): Unit = propertyEditor(config, default, config.flowUpdates(), descriptor, scope, key = key) public fun Element.configEditor( config: Config, @@ -271,8 +272,6 @@ public fun Element.configEditor( default: Meta? = null, key: Any? = null, scope: CoroutineScope? = null, -) { - render(this) { - configEditor(config,descriptor,default, key, scope) - } +): Unit = render(this) { + configEditor(config, default, descriptor, key, scope) } \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt index 7012698d..7363d67e 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt @@ -4,7 +4,6 @@ import hep.dataforge.meta.MetaItem import hep.dataforge.meta.MutableItemProvider import hep.dataforge.meta.descriptors.Described import hep.dataforge.meta.descriptors.NodeDescriptor -import hep.dataforge.meta.descriptors.get import hep.dataforge.names.Name import hep.dataforge.names.asName import hep.dataforge.names.toName @@ -33,13 +32,13 @@ public interface Vision : Described { /** * Get property. - * @param inherit toggles parent node property lookup - * @param includeStyles toggles inclusion of + * @param inherit toggles parent node property lookup. Null means default inheritance. + * @param includeStyles toggles inclusion of. Null means default style inclusion. */ public fun getProperty( name: Name, - inherit: Boolean = true, - includeStyles: Boolean = true, + inherit: Boolean? = null, + includeStyles: Boolean? = null, includeDefaults: Boolean = true, ): MetaItem<*>? @@ -76,6 +75,16 @@ public interface Vision : Described { } } +/** + * Own properties, excluding inheritance, styles and descriptor + */ +public val Vision.ownProperties: MutableItemProvider + get() = object : MutableItemProvider { + override fun getItem(name: Name): MetaItem<*>? = getOwnProperty(name) + 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 @@ -85,16 +94,12 @@ public fun Vision.allProperties( includeStyles: Boolean? = null, includeDefaults: Boolean = true, ): MutableItemProvider = object : MutableItemProvider { - override fun getItem(name: Name): MetaItem<*>? { - val actualInherit = inherit ?: descriptor?.get(name)?.inherited ?: false - val actualUseStyles = includeStyles ?: descriptor?.get(name)?.usesStyles ?: true - return getProperty( - name, - inherit = actualInherit, - includeStyles = actualUseStyles, - includeDefaults = includeDefaults - ) - } + override fun getItem(name: Name): MetaItem<*>? = getProperty( + name, + inherit = inherit, + includeStyles = includeStyles, + includeDefaults = includeDefaults + ) override fun setItem(name: Name, item: MetaItem<*>?): Unit = setProperty(name, item) } @@ -104,8 +109,8 @@ public fun Vision.allProperties( */ public fun Vision.getProperty( key: String, - inherit: Boolean = true, - includeStyles: Boolean = true, + inherit: Boolean? = null, + includeStyles: Boolean? = null, includeDefaults: Boolean = true, ): MetaItem<*>? = getProperty(key.toName(), inherit, includeStyles, includeDefaults) diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt index c608e4c4..17f08888 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt @@ -59,15 +59,15 @@ public open class VisionBase : Vision { override fun getProperty( name: Name, - inherit: Boolean, - includeStyles: Boolean, + inherit: Boolean?, + includeStyles: Boolean?, includeDefaults: Boolean, ): MetaItem<*>? = sequence { yield(getOwnProperty(name)) - if (includeStyles) { + if (includeStyles ?: descriptor?.get(name)?.usesStyles != false) { yieldAll(getStyleItems(name)) } - if (inherit) { + if (inherit ?: descriptor?.get(name)?.inherited == true) { yield(parent?.getProperty(name, inherit)) } yield(descriptor?.get(name)?.defaultItem()) diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt index 77aa950c..355d2b7d 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt @@ -125,8 +125,8 @@ internal class Prototypes( override fun getProperty( name: Name, - inherit: Boolean, - includeStyles: Boolean, + inherit: Boolean?, + includeStyles: Boolean?, includeDefaults: Boolean, ): MetaItem<*>? = null diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidMaterial.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidMaterial.kt index f060afea..7c69d283 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidMaterial.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidMaterial.kt @@ -114,16 +114,12 @@ public class SolidMaterial : Scheme() { public val Solid.color: ColorAccessor get() = ColorAccessor( - allProperties( - inherit = true, - includeStyles = true, - includeDefaults = true - ), + allProperties(inherit = true), MATERIAL_COLOR_KEY ) public var Solid.material: SolidMaterial? - get() = getProperty(MATERIAL_KEY).node?.let { SolidMaterial.read(it) } + get() = getProperty(MATERIAL_KEY, inherit = true).node?.let { SolidMaterial.read(it) } set(value) = setProperty(MATERIAL_KEY, value?.config) @VisionBuilder @@ -141,7 +137,7 @@ public fun Solid.material(builder: SolidMaterial.() -> Unit) { } public var Solid.opacity: Number? - get() = getProperty(MATERIAL_OPACITY_KEY).number + get() = getProperty(MATERIAL_OPACITY_KEY, inherit = true).number set(value) { setProperty(MATERIAL_OPACITY_KEY, value?.asValue()) } \ No newline at end of file diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidReference.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidReference.kt index 5378f71d..4512cf54 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidReference.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidReference.kt @@ -2,6 +2,7 @@ package hep.dataforge.vision.solid import hep.dataforge.meta.* import hep.dataforge.meta.descriptors.NodeDescriptor +import hep.dataforge.meta.descriptors.get import hep.dataforge.names.* import hep.dataforge.vision.* import kotlinx.coroutines.flow.Flow @@ -14,6 +15,24 @@ public interface SolidReference : Vision { public val prototype: Solid } +private fun SolidReference.getRefProperty( + name: Name, + inherit: Boolean?, + includeStyles: Boolean?, + includeDefaults: Boolean, +): MetaItem<*>? { + return sequence { + yield(getOwnProperty(name)) + if (includeStyles ?: descriptor?.get(name)?.usesStyles != false) { + yieldAll(getStyleItems(name)) + } + yield(prototype.getProperty(name, inherit, includeStyles, includeDefaults)) + if (inherit ?: descriptor?.get(name)?.inherited == true) { + yield(parent?.getProperty(name, inherit)) + } + }.merge() +} + /** * A reference [Solid] to reuse a template object */ @@ -60,19 +79,10 @@ public class SolidReferenceGroup( override fun getProperty( name: Name, - inherit: Boolean, - includeStyles: Boolean, + inherit: Boolean?, + includeStyles: Boolean?, includeDefaults: Boolean, - ): MetaItem<*>? = sequence { - yield(getOwnProperty(name)) - if (includeStyles) { - yieldAll(getStyleItems(name)) - } - yield(prototype.getProperty(name, inherit, includeStyles, includeDefaults)) - if (inherit) { - yield(parent?.getProperty(name, inherit)) - } - }.merge() + ): MetaItem<*>? = getRefProperty(name, inherit, includeStyles, includeDefaults) override fun attachChildren() { //do nothing @@ -104,19 +114,10 @@ public class SolidReferenceGroup( override fun getProperty( name: Name, - inherit: Boolean, - includeStyles: Boolean, + inherit: Boolean?, + includeStyles: Boolean?, includeDefaults: Boolean, - ): MetaItem<*>? = sequence { - yield(getOwnProperty(name)) - if (includeStyles) { - yieldAll(getStyleItems(name)) - } - yield(prototype.getProperty(name, inherit, includeStyles, includeDefaults)) - if (inherit) { - yield(parent?.getProperty(name, inherit)) - } - }.merge() + ): MetaItem<*>? = getRefProperty(name, inherit, includeStyles, includeDefaults) override var parent: VisionGroup? get() { diff --git a/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/PropertyTest.kt b/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/PropertyTest.kt index 30917b90..5f6fad96 100644 --- a/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/PropertyTest.kt +++ b/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/PropertyTest.kt @@ -17,7 +17,7 @@ class PropertyTest { box = box(100, 100, 100) } } - assertEquals(22, box?.getProperty("test").int) + assertEquals(22, box?.getProperty("test", inherit = true).int) } @Test From ccb916cff7c9f2cd041c411e8f451ebbf68948c9 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 19 Dec 2020 17:44:24 +0300 Subject: [PATCH 07/19] Encapsulate prototypes. --- .../main/kotlin/ru/mipt/npm/sat/satServer.kt | 5 +- .../kotlin/hep/dataforge/vision/VisionBase.kt | 9 +- .../hep/dataforge/vision/VisionGroupBase.kt | 6 +- .../hep/dataforge/vision/solid/SolidGroup.kt | 32 +++--- .../dataforge/vision/solid/SolidMaterial.kt | 18 ++- .../vision/solid/three/ThreePlugin.kt | 106 +++++++++--------- 6 files changed, 96 insertions(+), 80 deletions(-) diff --git a/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/satServer.kt b/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/satServer.kt index 685970bd..bd7abf90 100644 --- a/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/satServer.kt +++ b/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/satServer.kt @@ -39,9 +39,10 @@ fun main() { val randomI = Random.nextInt(1, 4) val randomJ = Random.nextInt(1, 4) val target = "layer[$randomLayer].segment[$randomI,$randomJ]".toName() - (sat[target] as? Solid)?.color("red") + val targetVision = sat[target] as Solid + targetVision.color("red") delay(300) - (sat[target] as? Solid)?.color("green") + targetVision.color("green") delay(10) } } diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt index 17f08888..94dbd2d8 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt @@ -11,8 +11,10 @@ import hep.dataforge.names.Name import hep.dataforge.names.asName import hep.dataforge.values.ValueType import hep.dataforge.vision.Vision.Companion.STYLE_KEY +import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.launch import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.Transient @@ -68,7 +70,7 @@ public open class VisionBase : Vision { yieldAll(getStyleItems(name)) } if (inherit ?: descriptor?.get(name)?.inherited == true) { - yield(parent?.getProperty(name, inherit)) + yield(parent?.getProperty(name, inherit, includeStyles, includeDefaults)) } yield(descriptor?.get(name)?.defaultItem()) }.merge() @@ -101,8 +103,9 @@ public open class VisionBase : Vision { if (propertyName == STYLE_KEY) { updateStyles(styles) } - - _propertyInvalidationFlow.tryEmit(propertyName) + GlobalScope.launch { + _propertyInvalidationFlow.emit(propertyName) + } } public fun configure(block: MutableMeta<*>.() -> Unit) { diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionGroupBase.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionGroupBase.kt index 0bb40007..5162809e 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionGroupBase.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionGroupBase.kt @@ -1,8 +1,10 @@ package hep.dataforge.vision import hep.dataforge.names.* +import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.launch import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.Transient @@ -43,7 +45,9 @@ public open class VisionGroupBase : VisionBase(), MutableVisionGroup { * Propagate children change event upwards */ private fun childrenChanged(name: NameToken, before: Vision?, after: Vision?) { - _structureChanges.tryEmit(MutableVisionGroup.StructureChange(name, before, after)) + GlobalScope.launch { + _structureChanges.emit(MutableVisionGroup.StructureChange(name, before, after)) + } } /** diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt index 355d2b7d..baf79443 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt @@ -16,7 +16,11 @@ import kotlinx.serialization.encoding.Encoder public interface PrototypeHolder { public val parent: VisionGroup? - public val prototypes: MutableVisionGroup? + + @VisionBuilder + public fun prototypes(builder: VisionContainerBuilder.() -> Unit) + + public fun getPrototype(name: Name): Solid? } /** @@ -28,18 +32,24 @@ public class SolidGroup : VisionGroupBase(), Solid, PrototypeHolder { override val descriptor: NodeDescriptor get() = Solid.descriptor + /** * A container for templates visible inside this group */ @Serializable(Prototypes.Companion::class) - override var prototypes: MutableVisionGroup? = null - private set + @SerialName("prototypes") + internal var prototypes: MutableVisionGroup? = null + + /** + * Ger a prototype redirecting the request to the parent if prototype is not found + */ + override fun getPrototype(name: Name): Solid? = + prototypes?.get(name) as? Solid ?: (parent as? PrototypeHolder)?.getPrototype(name) /** * Create or edit prototype node as a group */ - @VisionBuilder - public fun prototypes(builder: VisionContainerBuilder.() -> Unit): Unit { + override fun prototypes(builder: VisionContainerBuilder.() -> Unit): Unit { (prototypes ?: Prototypes().also { prototypes = it it.parent = this @@ -81,12 +91,6 @@ public fun SolidGroup(block: SolidGroup.() -> Unit): SolidGroup { return SolidGroup().apply(block) } -/** - * Ger a prototype redirecting the request to the parent if prototype is not found - */ -public tailrec fun PrototypeHolder.getPrototype(name: Name): Solid? = - prototypes?.get(name) as? Solid ?: (parent as? PrototypeHolder)?.getPrototype(name) - @VisionBuilder public fun VisionContainerBuilder.group( name: Name = Name.EMPTY, @@ -106,14 +110,12 @@ public fun VisionContainerBuilder.group(name: String, action: SolidGroup @Serializable(Prototypes.Companion::class) internal class Prototypes( children: Map = emptyMap(), -) : VisionGroupBase(), PrototypeHolder { +) : VisionGroupBase() { init { childrenInternal.putAll(children) } - override val prototypes: MutableVisionGroup get() = this - override fun attachChildren() { children.values.forEach { it.parent = parent @@ -131,7 +133,7 @@ internal class Prototypes( ): MetaItem<*>? = null override fun setProperty(name: Name, item: MetaItem<*>?, notify: Boolean) { - TODO("Not yet implemented") + error("Can't ser property of prototypes container") } override val descriptor: NodeDescriptor? = null diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidMaterial.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidMaterial.kt index 7c69d283..e1f2c940 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidMaterial.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidMaterial.kt @@ -90,10 +90,20 @@ public class SolidMaterial : Scheme() { //must be lazy to avoid initialization bug NodeDescriptor { value(COLOR_KEY) { + inherited = true + usesStyles = true type(ValueType.STRING, ValueType.NUMBER) widgetType = "color" } +// value(SPECULAR_COLOR_KEY) { +// inherited = true +// usesStyles = true +// type(ValueType.STRING, ValueType.NUMBER) +// widgetType = "color" +// } value(OPACITY_KEY) { + inherited = true + usesStyles = true type(ValueType.NUMBER) default(1.0) attributes { @@ -104,6 +114,8 @@ public class SolidMaterial : Scheme() { widgetType = "slider" } value(WIREFRAME_KEY) { + inherited = true + usesStyles = true type(ValueType.BOOLEAN) default(false) } @@ -124,11 +136,7 @@ public var Solid.material: SolidMaterial? @VisionBuilder public fun Solid.material(builder: SolidMaterial.() -> Unit) { - val node = allProperties( - inherit = true, - includeStyles = true, - includeDefaults = true - ).getItem(MATERIAL_KEY).node + val node = allProperties(inherit = true).getItem(MATERIAL_KEY).node if (node != null) { SolidMaterial.update(node, builder) } else { diff --git a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreePlugin.kt b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreePlugin.kt index 2732d624..834a56a7 100644 --- a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreePlugin.kt +++ b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreePlugin.kt @@ -46,42 +46,41 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer { as ThreeFactory? } - public fun buildObject3D(obj: Solid): Object3D { - return when (obj) { - is ThreeVision -> obj.render(this) - is SolidReferenceGroup -> ThreeReferenceFactory(this, obj) - is SolidGroup -> { - val group = ThreeGroup() - obj.children.forEach { (token, child) -> - if (child is Solid && child.ignore != true) { - try { - val object3D = buildObject3D(child) - group[token] = object3D - } catch (ex: Throwable) { - logger.error(ex) { "Failed to render $child" } - ex.printStackTrace() - } + public fun buildObject3D(obj: Solid): Object3D = when (obj) { + is ThreeVision -> obj.render(this) + is SolidReferenceGroup -> ThreeReferenceFactory(this, obj) + is SolidGroup -> { + val group = ThreeGroup() + obj.children.forEach { (token, child) -> + if (child is Solid && child.ignore != true) { + try { + val object3D = buildObject3D(child) + group[token] = object3D + } catch (ex: Throwable) { + logger.error(ex) { "Failed to render $child" } + ex.printStackTrace() } } + } - group.apply { - updatePosition(obj) - //obj.onChildrenChange() + group.apply { + updatePosition(obj) + //obj.onChildrenChange() - obj.propertyNameFlow.onEach { name -> - if ( - name.startsWith(Solid.POSITION_KEY) || - name.startsWith(Solid.ROTATION) || - name.startsWith(Solid.SCALE_KEY) - ) { - //update position of mesh using this object - updatePosition(obj) - } else if (name == Vision.VISIBLE_KEY) { - visible = obj.visible ?: true - } - }.launchIn(updateScope) + obj.propertyNameFlow.onEach { name -> + if ( + name.startsWith(Solid.POSITION_KEY) || + name.startsWith(Solid.ROTATION) || + name.startsWith(Solid.SCALE_KEY) + ) { + //update position of mesh using this object + updatePosition(obj) + } else if (name == Vision.VISIBLE_KEY) { + visible = obj.visible ?: true + } + }.launchIn(updateScope) - obj.structureChanges.onEach { (nameToken, _, child) -> + obj.structureChanges.onEach { (nameToken, _, child) -> // if (name.isEmpty()) { // logger.error { "Children change with empty name on $group" } // return@onChildrenChange @@ -90,32 +89,31 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer { // val parentName = name.cutLast() // val childName = name.last()!! - //removing old object - findChild(nameToken.asName())?.let { oldChild -> - oldChild.parent?.remove(oldChild) - } + //removing old object + findChild(nameToken.asName())?.let { oldChild -> + oldChild.parent?.remove(oldChild) + } - //adding new object - if (child != null && child is Solid) { - try { - val object3D = buildObject3D(child) - set(nameToken, object3D) - } catch (ex: Throwable) { - logger.error(ex) { "Failed to render $child" } - } + //adding new object + if (child != null && child is Solid) { + try { + val object3D = buildObject3D(child) + set(nameToken, object3D) + } catch (ex: Throwable) { + logger.error(ex) { "Failed to render $child" } } - }.launchIn(updateScope) - } + } + }.launchIn(updateScope) } - is Composite -> compositeFactory(this, obj) - else -> { - //find specialized factory for this type if it is present - val factory: ThreeFactory? = findObjectFactory(obj::class) - when { - factory != null -> factory(this, obj) - obj is GeometrySolid -> ThreeShapeFactory(this, obj) - else -> error("Renderer for ${obj::class} not found") - } + } + is Composite -> compositeFactory(this, obj) + else -> { + //find specialized factory for this type if it is present + val factory: ThreeFactory? = findObjectFactory(obj::class) + when { + factory != null -> factory(this, obj) + obj is GeometrySolid -> ThreeShapeFactory(this, obj) + else -> error("Renderer for ${obj::class} not found") } } } From 4c235b0f53faf1fb6776ec5249589da571269d55 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 19 Dec 2020 20:40:22 +0300 Subject: [PATCH 08/19] Fix color inheritance issues --- .../commonMain/kotlin/hep/dataforge/vision/Vision.kt | 11 +++++++++-- .../kotlin/hep/dataforge/vision/VisionBase.kt | 3 +-- .../kotlin/hep/dataforge/vision/VisionGroupBase.kt | 3 +-- .../kotlin/hep/dataforge/vision/solid/FX3DPlugin.kt | 2 +- .../kotlin/hep/dataforge/vision/solid/SolidGroup.kt | 2 -- .../dataforge/vision/solid/three/ThreeMaterials.kt | 2 +- 6 files changed, 13 insertions(+), 10 deletions(-) diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt index 7363d67e..abf24bc1 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt @@ -9,6 +9,8 @@ import hep.dataforge.names.asName import hep.dataforge.names.toName import hep.dataforge.provider.Type import hep.dataforge.vision.Vision.Companion.TYPE +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.flow.Flow import kotlinx.serialization.Transient @@ -24,6 +26,11 @@ public interface Vision : Described { @Transient public var parent: VisionGroup? + /** + * 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)`. @@ -32,8 +39,8 @@ public interface Vision : Described { /** * Get property. - * @param inherit toggles parent node property lookup. Null means default inheritance. - * @param includeStyles toggles inclusion of. Null means default style inclusion. + * @param inherit toggles parent node property lookup. Null means inference from descriptor. Default is false. + * @param includeStyles toggles inclusion of. Null means inference from descriptor. Default is true. */ public fun getProperty( name: Name, diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt index 94dbd2d8..27c9c92e 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt @@ -11,7 +11,6 @@ import hep.dataforge.names.Name import hep.dataforge.names.asName import hep.dataforge.values.ValueType import hep.dataforge.vision.Vision.Companion.STYLE_KEY -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.launch @@ -103,7 +102,7 @@ public open class VisionBase : Vision { if (propertyName == STYLE_KEY) { updateStyles(styles) } - GlobalScope.launch { + scope.launch { _propertyInvalidationFlow.emit(propertyName) } } diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionGroupBase.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionGroupBase.kt index 5162809e..2d4f271f 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionGroupBase.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionGroupBase.kt @@ -1,7 +1,6 @@ package hep.dataforge.vision import hep.dataforge.names.* -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.launch @@ -45,7 +44,7 @@ public open class VisionGroupBase : VisionBase(), MutableVisionGroup { * Propagate children change event upwards */ private fun childrenChanged(name: NameToken, before: Vision?, after: Vision?) { - GlobalScope.launch { + scope.launch { _structureChanges.emit(MutableVisionGroup.StructureChange(name, before, after)) } } diff --git a/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/FX3DPlugin.kt b/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/FX3DPlugin.kt index b635485b..0032f83a 100644 --- a/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/FX3DPlugin.kt +++ b/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/FX3DPlugin.kt @@ -71,7 +71,7 @@ class FX3DPlugin : AbstractPlugin() { is PolyLine -> PolyLine3D( obj.points.map { Point3D(it.x, it.y, it.z) }, obj.thickness.toFloat(), - obj.getProperty(SolidMaterial.MATERIAL_COLOR_KEY)?.color() + obj.getProperty(SolidMaterial.MATERIAL_COLOR_KEY, inherit = true)?.color() ).apply { this.meshView.cullFace = CullFace.FRONT } diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt index baf79443..e472bbd0 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt @@ -15,8 +15,6 @@ import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder public interface PrototypeHolder { - public val parent: VisionGroup? - @VisionBuilder public fun prototypes(builder: VisionContainerBuilder.() -> Unit) diff --git a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeMaterials.kt b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeMaterials.kt index f7ec681d..4401e8cd 100644 --- a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeMaterials.kt +++ b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeMaterials.kt @@ -76,7 +76,7 @@ public object ThreeMaterials { } public fun getMaterial(vision3D: Vision, cache: Boolean): Material { - val meta = vision3D.getProperty(SolidMaterial.MATERIAL_KEY).node ?: return DEFAULT + val meta = vision3D.getProperty(SolidMaterial.MATERIAL_KEY, inherit = true).node ?: return DEFAULT return if (cache) { materialCache.getOrPut(meta) { buildMaterial(meta) } } else { From 0259d4eb152a7fdba2cd74bb1f998a0485cb230c Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 20 Dec 2020 21:43:29 +0300 Subject: [PATCH 09/19] Another huge refactoring of property updates --- .../main/kotlin/ru/mipt/npm/sat/satServer.kt | 2 +- .../vision/solid/demo/VariableBox.kt | 7 +-- .../vision/bootstrap/visionPropertyEditor.kt | 3 +- .../dataforge/vision/react/PropertyEditor.kt | 6 +- .../kotlin/hep/dataforge/vision/StyleSheet.kt | 5 +- .../kotlin/hep/dataforge/vision/Vision.kt | 26 +++++++-- .../kotlin/hep/dataforge/vision/VisionBase.kt | 31 +++++++---- .../hep/dataforge/vision/VisionChange.kt | 7 ++- .../hep/dataforge/vision/VisionGroupBase.kt | 2 +- .../kotlin/hep/dataforge/vision/misc.kt | 12 ---- .../hep/dataforge/vision/valueWidget.kt | 27 --------- .../hep/dataforge/vision/visionDescriptor.kt | 55 +++++++++++++++++-- .../dataforge/vision/client/VisionClient.kt | 48 ++++++++++------ .../vision/solid/FXReferenceFactory.kt | 6 +- .../vision/solid/VisualObjectFXBinding.kt | 6 +- .../vision/three/server/VisionServer.kt | 40 ++++++++++++-- .../hep/dataforge/vision/solid/Solid.kt | 24 ++++---- .../hep/dataforge/vision/solid/SolidGroup.kt | 10 +++- .../dataforge/vision/solid/SolidMaterial.kt | 18 ++++-- .../dataforge/vision/solid/SolidReference.kt | 17 +++--- .../vision/solid/three/MeshThreeFactory.kt | 6 +- .../vision/solid/three/ThreeFactory.kt | 2 +- .../vision/solid/three/ThreeLabelFactory.kt | 6 +- .../vision/solid/three/ThreeLineFactory.kt | 6 +- .../vision/solid/three/ThreeMaterials.kt | 4 ++ .../vision/solid/three/ThreePlugin.kt | 4 +- .../solid/three/ThreeReferenceFactory.kt | 6 +- 27 files changed, 238 insertions(+), 148 deletions(-) delete mode 100644 visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/valueWidget.kt diff --git a/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/satServer.kt b/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/satServer.kt index bd7abf90..761cd982 100644 --- a/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/satServer.kt +++ b/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/satServer.kt @@ -42,7 +42,7 @@ fun main() { val targetVision = sat[target] as Solid targetVision.color("red") delay(300) - targetVision.color("green") + targetVision.color("darkgreen") delay(10) } } diff --git a/demo/spatial-showcase/src/jsMain/kotlin/hep/dataforge/vision/solid/demo/VariableBox.kt b/demo/spatial-showcase/src/jsMain/kotlin/hep/dataforge/vision/solid/demo/VariableBox.kt index 366acfc4..2910e0d3 100644 --- a/demo/spatial-showcase/src/jsMain/kotlin/hep/dataforge/vision/solid/demo/VariableBox.kt +++ b/demo/spatial-showcase/src/jsMain/kotlin/hep/dataforge/vision/solid/demo/VariableBox.kt @@ -16,8 +16,6 @@ import info.laht.threekt.core.BufferGeometry import info.laht.threekt.core.Object3D import info.laht.threekt.geometries.BoxBufferGeometry import info.laht.threekt.objects.Mesh -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach import kotlin.math.max internal fun SolidGroup.varBox( @@ -62,7 +60,7 @@ internal class VariableBox(xSize: Number, ySize: Number, zSize: Number) : ThreeV mesh.scale.set(xSize, ySize, zSize) //add listener to object properties - propertyNameFlow.onEach { name -> + onPropertyChange(three.context) { name -> when { name.startsWith(GEOMETRY_KEY) -> { val newXSize = getProperty(X_SIZE_KEY, false).number?.toDouble() ?: 1.0 @@ -78,7 +76,8 @@ internal class VariableBox(xSize: Number, ySize: Number, zSize: Number) : ThreeV } else -> mesh.updateProperty(this@VariableBox, name) } - }.launchIn(three.context) + } + return mesh } diff --git a/ui/bootstrap/src/main/kotlin/hep/dataforge/vision/bootstrap/visionPropertyEditor.kt b/ui/bootstrap/src/main/kotlin/hep/dataforge/vision/bootstrap/visionPropertyEditor.kt index 14a93eb8..b9a30bc8 100644 --- a/ui/bootstrap/src/main/kotlin/hep/dataforge/vision/bootstrap/visionPropertyEditor.kt +++ b/ui/bootstrap/src/main/kotlin/hep/dataforge/vision/bootstrap/visionPropertyEditor.kt @@ -13,11 +13,12 @@ public fun RBuilder.visionPropertyEditor( descriptor: NodeDescriptor? = vision.descriptor, key: Any? = null, ) { + card("Properties") { propertyEditor( provider = vision.ownProperties, defaultProvider = vision.allProperties(), - updateFlow = vision.propertyNameFlow, + updateFlow = vision.propertyChanges, descriptor = descriptor, key = key) } diff --git a/ui/react/src/main/kotlin/hep/dataforge/vision/react/PropertyEditor.kt b/ui/react/src/main/kotlin/hep/dataforge/vision/react/PropertyEditor.kt index c730123b..ebd62473 100644 --- a/ui/react/src/main/kotlin/hep/dataforge/vision/react/PropertyEditor.kt +++ b/ui/react/src/main/kotlin/hep/dataforge/vision/react/PropertyEditor.kt @@ -7,6 +7,7 @@ import hep.dataforge.names.NameToken import hep.dataforge.names.lastOrNull import hep.dataforge.names.plus import hep.dataforge.values.Value +import hep.dataforge.vision.hidden import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.channels.awaitClose @@ -66,6 +67,9 @@ private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) { val itemName by useState { props.name ?: Name.EMPTY } var item: MetaItem<*>? by useState { props.provider.getItem(itemName) } val descriptorItem: ItemDescriptor? = props.descriptor?.get(itemName) + + if(descriptorItem?.hidden == true) return //fail fast for hidden property + var actualItem: MetaItem? by useState { item ?: props.defaultProvider?.getItem(itemName) ?: descriptorItem?.defaultItem() } @@ -106,8 +110,6 @@ private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) { update() } - - if (actualItem is MetaItem.NodeItem) { styledDiv { css { diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/StyleSheet.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/StyleSheet.kt index 783b376c..d90fe655 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/StyleSheet.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/StyleSheet.kt @@ -5,6 +5,7 @@ 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 @@ -54,7 +55,9 @@ internal fun Vision.styleChanged(key: String, oldStyle: Meta?, newStyle: Meta?) val tokens: Collection = ((oldStyle?.items?.keys ?: emptySet()) + (newStyle?.items?.keys ?: emptySet())) .map { it.asName() } - tokens.forEach { parent?.notifyPropertyChanged(it) } + parent?.scope?.launch { + tokens.forEach { parent?.notifyPropertyChanged(it) } + } } if (this is VisionGroup) { for (obj in this) { diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt index abf24bc1..ba585b46 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt @@ -9,9 +9,10 @@ import hep.dataforge.names.asName import hep.dataforge.names.toName import hep.dataforge.provider.Type import hep.dataforge.vision.Vision.Companion.TYPE -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.* +import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.callbackFlow import kotlinx.serialization.Transient /** @@ -29,7 +30,7 @@ public interface Vision : Described { /** * A coroutine scope for asynchronous calls and locks */ - public val scope: CoroutineScope get() = parent?.scope?: GlobalScope + public val scope: CoroutineScope get() = parent?.scope ?: GlobalScope /** * A fast accessor method to get own property (no inheritance or styles). @@ -55,17 +56,26 @@ public interface Vision : Described { */ public fun setProperty(name: Name, item: MetaItem<*>?, notify: Boolean = true) + 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. */ - public val propertyNameFlow: Flow + public val propertyChanges: Flow get() = callbackFlow { + coroutineScope { + onPropertyChange(this) { + send(it) + } + awaitClose { cancel() } + } + } /** * Notify all listeners that a property has been changed and should be invalidated */ - public fun notifyPropertyChanged(propertyName: Name): Unit + public suspend fun notifyPropertyChanged(propertyName: Name): Unit /** * Update this vision using external meta. Children are not updated. @@ -82,6 +92,12 @@ public interface Vision : Described { } } +public fun Vision.asyncNotifyPropertyChange(propertyName: Name){ + scope.launch { + notifyPropertyChanged(propertyName) + } +} + /** * Own properties, excluding inheritance, styles and descriptor */ diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt index 27c9c92e..141366d1 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt @@ -11,8 +11,11 @@ import hep.dataforge.names.Name import hep.dataforge.names.asName import hep.dataforge.values.ValueType import hep.dataforge.vision.Vision.Companion.STYLE_KEY +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -44,7 +47,9 @@ public open class VisionBase : Vision { properties = newProperties newProperties.onChange(this) { name, oldItem, newItem -> if (oldItem != newItem) { - notifyPropertyChanged(name) + scope.launch { + notifyPropertyChanged(name) + } } } } @@ -77,14 +82,16 @@ public open class VisionBase : Vision { @Synchronized override fun setProperty(name: Name, item: MetaItem<*>?, notify: Boolean) { getOrCreateConfig().setItem(name, item) - if(notify) { - notifyPropertyChanged(name) + if (notify) { + scope.launch { + notifyPropertyChanged(name) + } } } override val descriptor: NodeDescriptor? get() = null - private fun updateStyles(names: List) { + private suspend fun updateStyles(names: List) { names.mapNotNull { getStyle(it) }.asSequence() .flatMap { it.items.asSequence() } .distinctBy { it.key } @@ -94,17 +101,19 @@ public open class VisionBase : Vision { } @Transient - private val _propertyInvalidationFlow: MutableSharedFlow = MutableSharedFlow() + private val propertyInvalidationFlow: MutableSharedFlow = MutableSharedFlow() - override val propertyNameFlow: SharedFlow get() = _propertyInvalidationFlow + override val propertyChanges: Flow get() = propertyInvalidationFlow - override fun notifyPropertyChanged(propertyName: Name) { + override fun onPropertyChange(scope: CoroutineScope, callback: suspend (Name) -> Unit) { + propertyInvalidationFlow.onEach(callback).launchIn(scope) + } + + override suspend fun notifyPropertyChanged(propertyName: Name) { if (propertyName == STYLE_KEY) { updateStyles(styles) } - scope.launch { - _propertyInvalidationFlow.emit(propertyName) - } + propertyInvalidationFlow.emit(propertyName) } public fun configure(block: MutableMeta<*>.() -> Unit) { diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionChange.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionChange.kt index c2717108..7dddd655 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionChange.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionChange.kt @@ -67,6 +67,9 @@ public class VisionChange( @Serializable(MetaSerializer::class) public val properties: Meta? = null, public val children: Map? = null, ) { + init { + (vision as? VisionGroup)?.attachChildren() + } } @@ -81,10 +84,10 @@ private fun CoroutineScope.collectChange( ) { //Collect properties change - source.propertyNameFlow.onEach { propertyName -> + source.onPropertyChange(this) { propertyName -> val newItem = source.getProperty(propertyName, inherit = false, includeStyles = false, includeDefaults = false) collector().propertyChanged(name, propertyName, newItem) - }.launchIn(this) + } if (source is VisionGroup) { //Subscribe for children changes diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionGroupBase.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionGroupBase.kt index 2d4f271f..0752b98b 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionGroupBase.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionGroupBase.kt @@ -28,7 +28,7 @@ public open class VisionGroupBase : VisionBase(), MutableVisionGroup { */ override val children: Map get() = childrenInternal - override fun notifyPropertyChanged(propertyName: Name) { + override suspend fun notifyPropertyChanged(propertyName: Name) { super.notifyPropertyChanged(propertyName) for (obj in this) { obj.notifyPropertyChanged(propertyName) diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/misc.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/misc.kt index 75cb8f40..ef1882fe 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/misc.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/misc.kt @@ -1,15 +1,11 @@ package hep.dataforge.vision import hep.dataforge.meta.* -import hep.dataforge.meta.descriptors.NodeDescriptor -import hep.dataforge.names.Name -import hep.dataforge.values.ValueType import hep.dataforge.values.asValue @DslMarker public annotation class VisionBuilder - public fun Sequence?>.merge(): MetaItem<*>? { return when (val first = firstOrNull { it != null }) { null -> null @@ -22,14 +18,6 @@ public fun Sequence?>.merge(): MetaItem<*>? { } } -public inline fun > NodeDescriptor.enum(key: Name, default: E?): Unit = value(key) { - type(ValueType.STRING) - default?.let { - default(default) - } - allowedValues = enumValues().map { it.asValue() } -} - @DFExperimental public val Vision.properties: Config? get() = (this as? VisionBase)?.properties diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/valueWidget.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/valueWidget.kt deleted file mode 100644 index f563d87c..00000000 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/valueWidget.kt +++ /dev/null @@ -1,27 +0,0 @@ -package hep.dataforge.vision - -import hep.dataforge.meta.* -import hep.dataforge.meta.descriptors.ValueDescriptor -import hep.dataforge.meta.descriptors.attributes - -/** - * Extension property to access the "widget" key of [ValueDescriptor] - */ -public var ValueDescriptor.widget: Meta - get() = attributes["widget"].node ?: Meta.EMPTY - set(value) { - attributes { - set("widget", value) - } - } - -/** - * Extension property to access the "widget.type" key of [ValueDescriptor] - */ -public var ValueDescriptor.widgetType: String? - get() = attributes["widget.type"].string - set(value) { - attributes{ - set("widget.type", value) - } - } \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/visionDescriptor.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/visionDescriptor.kt index 0e068827..59001d15 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/visionDescriptor.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/visionDescriptor.kt @@ -1,11 +1,13 @@ package hep.dataforge.vision -import hep.dataforge.meta.Meta -import hep.dataforge.meta.boolean +import hep.dataforge.meta.* import hep.dataforge.meta.descriptors.ItemDescriptor +import hep.dataforge.meta.descriptors.NodeDescriptor +import hep.dataforge.meta.descriptors.ValueDescriptor import hep.dataforge.meta.descriptors.attributes -import hep.dataforge.meta.get -import hep.dataforge.meta.set +import hep.dataforge.names.Name +import hep.dataforge.values.ValueType +import hep.dataforge.values.asValue private const val INHERITED_DESCRIPTOR_ATTRIBUTE = "inherited" private const val STYLE_DESCRIPTOR_ATTRIBUTE = "useStyles" @@ -30,3 +32,48 @@ public val Vision.describedProperties: Meta } } +/** + * Extension property to access the "widget" key of [ValueDescriptor] + */ +public var ValueDescriptor.widget: Meta + get() = attributes["widget"].node ?: Meta.EMPTY + set(value) { + attributes { + set("widget", value) + } + } + +/** + * Extension property to access the "widget.type" key of [ValueDescriptor] + */ +public var ValueDescriptor.widgetType: String? + get() = attributes["widget.type"].string + set(value) { + attributes { + set("widget.type", value) + } + } + +/** + * If true, this item is hidden in property editor. Default is false + */ +public val ItemDescriptor.hidden: Boolean + get() = attributes["widget.hide"].boolean ?: false + +public fun ItemDescriptor.hide(): Unit = attributes { + set("widget.hide", true) +} + + +public inline fun > NodeDescriptor.enum( + key: Name, + default: E?, + crossinline modifier: ValueDescriptor.() -> Unit = {}, +): Unit = value(key) { + type(ValueType.STRING) + default?.let { + default(default) + } + allowedValues = enumValues().map { it.asValue() } + modifier() +} \ No newline at end of file diff --git a/visionforge-core/src/jsMain/kotlin/hep/dataforge/vision/client/VisionClient.kt b/visionforge-core/src/jsMain/kotlin/hep/dataforge/vision/client/VisionClient.kt index eefffc17..36f0ee1f 100644 --- a/visionforge-core/src/jsMain/kotlin/hep/dataforge/vision/client/VisionClient.kt +++ b/visionforge-core/src/jsMain/kotlin/hep/dataforge/vision/client/VisionClient.kt @@ -18,9 +18,6 @@ import org.w3c.dom.WebSocket import org.w3c.dom.asList import org.w3c.dom.get import org.w3c.dom.url.URL -import kotlin.collections.HashMap -import kotlin.collections.forEach -import kotlin.collections.maxByOrNull import kotlin.collections.set import kotlin.reflect.KClass @@ -84,14 +81,18 @@ public class VisionClient : AbstractPlugin() { renderVision(element, embeddedVision, outputMeta) } - val endpoint = resolveEndpoint(element) - logger.info { "Vision server is resolved to $endpoint" } + element.attributes[OUTPUT_FETCH_ATTRIBUTE]?.let { attr -> - element.attributes[OUTPUT_FETCH_ATTRIBUTE]?.let { - - val fetchUrl = URL(endpoint).apply { + val fetchUrl = if (attr.value.isBlank() || attr.value == "auto") { + val endpoint = resolveEndpoint(element) + logger.info { "Vision server is resolved to $endpoint" } + URL(endpoint).apply { + pathname += "/vision" + } + } else { + URL(attr.value) + }.apply { searchParams.append("name", name) - pathname += "/vision" } logger.info { "Fetching vision data from $fetchUrl" } @@ -102,16 +103,22 @@ public class VisionClient : AbstractPlugin() { renderVision(element, vision, outputMeta) } } else { - logger.error { "Failed to fetch initial vision state from $endpoint" } + logger.error { "Failed to fetch initial vision state from $fetchUrl" } } } } - element.attributes[OUTPUT_CONNECT_ATTRIBUTE]?.let { - - val wsUrl = URL(endpoint).apply { - pathname += "/ws" + element.attributes[OUTPUT_CONNECT_ATTRIBUTE]?.let { attr -> + val wsUrl = if (attr.value.isBlank() || attr.value == "auto") { + val endpoint = resolveEndpoint(element) + logger.info { "Vision server is resolved to $endpoint" } + URL(endpoint).apply { + pathname += "/ws" + } + } else { + URL(attr.value) + }.apply { protocol = "ws" searchParams.append("name", name) } @@ -122,15 +129,20 @@ public class VisionClient : AbstractPlugin() { onmessage = { messageEvent -> val stringData: String? = messageEvent.data as? String if (stringData != null) { - val dif = visionManager.jsonFormat.decodeFromString( + val change = visionManager.jsonFormat.decodeFromString( VisionChange.serializer(), stringData ) - logger.debug { "Got update $dif for output with name $name" } - visionMap[element]?.update(dif) + + if(change.vision!= null){ + renderVision(element, change.vision, outputMeta) + } + + logger.debug { "Got update $change for output with name $name" } + visionMap[element]?.update(change) ?: console.info("Target vision for element $element with name $name not found") } else { - console.error ("WebSocket message data is not a string") + console.error("WebSocket message data is not a string") } } onopen = { diff --git a/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/FXReferenceFactory.kt b/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/FXReferenceFactory.kt index 9b15df72..14845845 100644 --- a/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/FXReferenceFactory.kt +++ b/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/FXReferenceFactory.kt @@ -4,8 +4,6 @@ import hep.dataforge.names.* import hep.dataforge.vision.Vision import javafx.scene.Group import javafx.scene.Node -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach import kotlin.reflect.KClass class FXReferenceFactory(val plugin: FX3DPlugin) : FX3DFactory { @@ -15,7 +13,7 @@ class FXReferenceFactory(val plugin: FX3DPlugin) : FX3DFactory + obj.onPropertyChange(plugin.context) { name-> if (name.firstOrNull()?.body == SolidReferenceGroup.REFERENCE_CHILD_PROPERTY_PREFIX) { val childName = name.firstOrNull()?.index?.toName() ?: error("Wrong syntax for reference child property: '$name'") val propertyName = name.cutFirst() @@ -23,7 +21,7 @@ class FXReferenceFactory(val plugin: FX3DPlugin) : FX3DFactory?>>() init { - obj.propertyNameFlow.onEach { name -> + obj.onPropertyChange(fx.context) { name -> bindings.filter { it.key.startsWith(name) }.forEach { entry -> Platform.runLater { entry.value.invalidate() @@ -30,7 +28,7 @@ class VisualObjectFXBinding(val fx: FX3DPlugin, val obj: Vision) { // bindings[currentName]?.invalidate() // currentName = currentName.cutLast() // } - }.launchIn(fx.context) + } } operator fun get(key: Name): ObjectBinding?> { diff --git a/visionforge-server/src/main/kotlin/hep/dataforge/vision/three/server/VisionServer.kt b/visionforge-server/src/main/kotlin/hep/dataforge/vision/three/server/VisionServer.kt index eaa38618..13528ff7 100644 --- a/visionforge-server/src/main/kotlin/hep/dataforge/vision/three/server/VisionServer.kt +++ b/visionforge-server/src/main/kotlin/hep/dataforge/vision/three/server/VisionServer.kt @@ -41,6 +41,13 @@ import java.awt.Desktop import java.net.URI import kotlin.time.milliseconds +public enum class VisionServerDataMode { + EMBED, + FETCH, + CONNECT +} + + /** * A ktor plugin container with given [routing] */ @@ -52,6 +59,7 @@ public class VisionServer internal constructor( override val config: Config = Config() public var updateInterval: Long by config.long(300, key = UPDATE_INTERVAL_KEY) public var cacheFragments: Boolean by config.boolean(true) + public var dataMode: VisionServerDataMode = VisionServerDataMode.CONNECT /** * a list of headers that should be applied to all pages @@ -72,10 +80,23 @@ public class VisionServer internal constructor( val consumer = object : VisionTagConsumer(consumer) { override fun DIV.renderVision(name: Name, vision: Vision, outputMeta: Meta) { visionMap[name] = vision - - // Toggle updates - attributes[OUTPUT_FETCH_ATTRIBUTE] = "true" - attributes[OUTPUT_CONNECT_ATTRIBUTE] = "true" + // Toggle update mode + when (dataMode) { + VisionServerDataMode.EMBED -> { + script { + attributes["class"] = OUTPUT_DATA_CLASS + unsafe { + +visionManager.encodeToString(vision) + } + } + } + VisionServerDataMode.FETCH -> { + attributes[OUTPUT_FETCH_ATTRIBUTE] = "auto" + } + VisionServerDataMode.CONNECT -> { + attributes[OUTPUT_CONNECT_ATTRIBUTE] = "auto" + } + } } } @@ -110,10 +131,19 @@ public class VisionServer internal constructor( application.log.debug("Opened server socket for $name") val vision: Vision = visions[name.toName()] ?: error("Plot with id='$name' not registered") + try { withContext(visionManager.context.coroutineContext) { + + val initialVision = VisionChange(vision = vision) + val initialJson = visionManager.jsonFormat.encodeToString( + VisionChange.serializer(), + initialVision + ) + outgoing.send(Frame.Text(initialJson)) + vision.flowChanges(visionManager, updateInterval.milliseconds).collect { update -> - val json = VisionManager.defaultJson.encodeToString( + val json = visionManager.jsonFormat.encodeToString( VisionChange.serializer(), update ) diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Solid.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Solid.kt index 65cf37e4..987fec3c 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Solid.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Solid.kt @@ -61,6 +61,7 @@ public interface Solid : Vision { public val descriptor: NodeDescriptor by lazy { NodeDescriptor { value(VISIBLE_KEY) { + inherited = false type(ValueType.BOOLEAN) default(true) } @@ -69,11 +70,14 @@ public interface Solid : Vision { value(Vision.STYLE_KEY) { type(ValueType.STRING) multiple = true + hide() } item(SolidMaterial.MATERIAL_KEY.toString(), SolidMaterial.descriptor) - enum(ROTATION_ORDER_KEY, default = RotationOrder.XYZ) + enum(ROTATION_ORDER_KEY, default = RotationOrder.XYZ) { + hide() + } } } @@ -152,21 +156,21 @@ public var Solid.x: Number get() = position?.x ?: 0f set(value) { position().x = value.toDouble() - notifyPropertyChanged(Solid.X_POSITION_KEY) + asyncNotifyPropertyChange(Solid.X_POSITION_KEY) } public var Solid.y: Number get() = position?.y ?: 0f set(value) { position().y = value.toDouble() - notifyPropertyChanged(Solid.Y_POSITION_KEY) + asyncNotifyPropertyChange(Solid.Y_POSITION_KEY) } public var Solid.z: Number get() = position?.z ?: 0f set(value) { position().z = value.toDouble() - notifyPropertyChanged(Solid.Z_POSITION_KEY) + asyncNotifyPropertyChange(Solid.Z_POSITION_KEY) } private fun Solid.rotation(): Point3D = @@ -176,21 +180,21 @@ public var Solid.rotationX: Number get() = rotation?.x ?: 0f set(value) { rotation().x = value.toDouble() - notifyPropertyChanged(Solid.X_ROTATION_KEY) + asyncNotifyPropertyChange(Solid.X_ROTATION_KEY) } public var Solid.rotationY: Number get() = rotation?.y ?: 0f set(value) { rotation().y = value.toDouble() - notifyPropertyChanged(Solid.Y_ROTATION_KEY) + asyncNotifyPropertyChange(Solid.Y_ROTATION_KEY) } public var Solid.rotationZ: Number get() = rotation?.z ?: 0f set(value) { rotation().z = value.toDouble() - notifyPropertyChanged(Solid.Z_ROTATION_KEY) + asyncNotifyPropertyChange(Solid.Z_ROTATION_KEY) } private fun Solid.scale(): Point3D = @@ -200,19 +204,19 @@ public var Solid.scaleX: Number get() = scale?.x ?: 1f set(value) { scale().x = value.toDouble() - notifyPropertyChanged(Solid.X_SCALE_KEY) + asyncNotifyPropertyChange(Solid.X_SCALE_KEY) } public var Solid.scaleY: Number get() = scale?.y ?: 1f set(value) { scale().y = value.toDouble() - notifyPropertyChanged(Solid.Y_SCALE_KEY) + asyncNotifyPropertyChange(Solid.Y_SCALE_KEY) } public var Solid.scaleZ: Number get() = scale?.z ?: 1f set(value) { scale().z = value.toDouble() - notifyPropertyChanged(Solid.Z_SCALE_KEY) + asyncNotifyPropertyChange(Solid.Z_SCALE_KEY) } \ No newline at end of file diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt index e472bbd0..c9d1654a 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt @@ -42,7 +42,7 @@ public class SolidGroup : VisionGroupBase(), Solid, PrototypeHolder { * Ger a prototype redirecting the request to the parent if prototype is not found */ override fun getPrototype(name: Name): Solid? = - prototypes?.get(name) as? Solid ?: (parent as? PrototypeHolder)?.getPrototype(name) + (prototypes?.get(name) as? Solid) ?: (parent as? PrototypeHolder)?.getPrototype(name) /** * Create or edit prototype node as a group @@ -108,7 +108,7 @@ public fun VisionContainerBuilder.group(name: String, action: SolidGroup @Serializable(Prototypes.Companion::class) internal class Prototypes( children: Map = emptyMap(), -) : VisionGroupBase() { +) : VisionGroupBase(), PrototypeHolder { init { childrenInternal.putAll(children) @@ -155,4 +155,10 @@ internal class Prototypes( mapSerializer.serialize(encoder, value.children) } } + + override fun prototypes(builder: VisionContainerBuilder.() -> Unit) { + apply(builder) + } + + override fun getPrototype(name: Name): Solid? = get(name) as? Solid } diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidMaterial.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidMaterial.kt index e1f2c940..d845df8b 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidMaterial.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidMaterial.kt @@ -89,18 +89,24 @@ public class SolidMaterial : Scheme() { public override val descriptor: NodeDescriptor by lazy { //must be lazy to avoid initialization bug NodeDescriptor { + inherited = true + usesStyles = true + value(COLOR_KEY) { inherited = true usesStyles = true type(ValueType.STRING, ValueType.NUMBER) widgetType = "color" } -// value(SPECULAR_COLOR_KEY) { -// inherited = true -// usesStyles = true -// type(ValueType.STRING, ValueType.NUMBER) -// widgetType = "color" -// } + + value(SPECULAR_COLOR_KEY) { + inherited = true + usesStyles = true + type(ValueType.STRING, ValueType.NUMBER) + widgetType = "color" + hide() + } + value(OPACITY_KEY) { inherited = true usesStyles = true diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidReference.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidReference.kt index 4512cf54..6430fc3d 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidReference.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidReference.kt @@ -5,9 +5,7 @@ import hep.dataforge.meta.descriptors.NodeDescriptor import hep.dataforge.meta.descriptors.get import hep.dataforge.names.* import hep.dataforge.vision.* -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.map +import kotlinx.coroutines.CoroutineScope import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -128,14 +126,15 @@ public class SolidReferenceGroup( error("Setting a parent for a reference child is not possible") } - override val propertyNameFlow: Flow - get() = this@SolidReferenceGroup.propertyNameFlow.filter { name -> - name.startsWith(childToken(childName)) - }.map { name -> - name.cutFirst() + override fun onPropertyChange(scope: CoroutineScope, callback: suspend (Name) -> Unit) { + this@SolidReferenceGroup.onPropertyChange(scope) { name -> + if (name.startsWith(childToken(childName))) { + callback(name.cutFirst()) + } } + } - override fun notifyPropertyChanged(propertyName: Name) { + override suspend fun notifyPropertyChanged(propertyName: Name) { this@SolidReferenceGroup.notifyPropertyChanged(childPropertyName(childName, propertyName)) } diff --git a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/MeshThreeFactory.kt b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/MeshThreeFactory.kt index 3adaf603..813086fd 100644 --- a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/MeshThreeFactory.kt +++ b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/MeshThreeFactory.kt @@ -15,8 +15,6 @@ 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 kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach import kotlin.reflect.KClass /** @@ -45,7 +43,7 @@ public abstract class MeshThreeFactory( }.applyProperties(obj) //add listener to object properties - obj.propertyNameFlow.onEach { name -> + obj.onPropertyChange(three.updateScope) { name -> when { name.startsWith(Solid.GEOMETRY_KEY) -> { val oldGeometry = mesh.geometry as BufferGeometry @@ -59,7 +57,7 @@ public abstract class MeshThreeFactory( name.startsWith(EDGES_KEY) -> mesh.applyEdges(obj) else -> mesh.updateProperty(obj, name) } - }.launchIn(three.updateScope) + } return mesh } diff --git a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeFactory.kt b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeFactory.kt index 33cfcc60..d6be69e1 100644 --- a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeFactory.kt +++ b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeFactory.kt @@ -47,7 +47,7 @@ public fun Object3D.updatePosition(obj: Vision) { */ public fun Object3D.updateProperty(source: Vision, propertyName: Name) { if (this is Mesh && propertyName.startsWith(MATERIAL_KEY)) { - this.material = getMaterial(source, false) + this.material = getMaterial(source, true) } else if ( propertyName.startsWith(Solid.POSITION_KEY) || propertyName.startsWith(Solid.ROTATION) diff --git a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeLabelFactory.kt b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeLabelFactory.kt index 4d1c7d99..258628bb 100644 --- a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeLabelFactory.kt +++ b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeLabelFactory.kt @@ -8,8 +8,6 @@ import info.laht.threekt.core.Object3D import info.laht.threekt.geometries.TextBufferGeometry import info.laht.threekt.objects.Mesh import kotlinext.js.jsObject -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach import kotlin.reflect.KClass /** @@ -27,10 +25,10 @@ public object ThreeLabelFactory : ThreeFactory { }) return Mesh(textGeo, getMaterial(obj, true)).apply { updatePosition(obj) - obj.propertyNameFlow.onEach { _ -> + obj.onPropertyChange(three.updateScope){ _ -> //TODO three.logger.warn{"Label parameter change not implemented"} - }.launchIn(three.updateScope) + } } } } \ No newline at end of file diff --git a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeLineFactory.kt b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeLineFactory.kt index d914a218..0d7a8e5c 100644 --- a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeLineFactory.kt +++ b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeLineFactory.kt @@ -9,8 +9,6 @@ import info.laht.threekt.core.Geometry import info.laht.threekt.core.Object3D import info.laht.threekt.math.Color import info.laht.threekt.objects.LineSegments -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach import kotlin.reflect.KClass public object ThreeLineFactory : ThreeFactory { @@ -30,9 +28,9 @@ public object ThreeLineFactory : ThreeFactory { updatePosition(obj) //layers.enable(obj.layer) //add listener to object properties - obj.propertyNameFlow.onEach { propertyName -> + obj.onPropertyChange(three.updateScope) { propertyName -> updateProperty(obj, propertyName) - }.launchIn(three.updateScope) + } } } diff --git a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeMaterials.kt b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeMaterials.kt index 4401e8cd..1736ce82 100644 --- a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeMaterials.kt +++ b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeMaterials.kt @@ -59,6 +59,10 @@ public object ThreeMaterials { MeshPhongMaterial().apply { color = meta[SolidMaterial.COLOR_KEY]?.getColor() ?: DEFAULT_COLOR specular = meta[SolidMaterial.SPECULAR_COLOR_KEY]!!.getColor() + emissive = specular + reflectivity = 1.0 + refractionRatio = 1.0 + shininess = 100.0 opacity = meta[SolidMaterial.OPACITY_KEY]?.double ?: 1.0 transparent = opacity < 1.0 wireframe = meta[SolidMaterial.WIREFRAME_KEY].boolean ?: false diff --git a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreePlugin.kt b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreePlugin.kt index 834a56a7..a7a74ce3 100644 --- a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreePlugin.kt +++ b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreePlugin.kt @@ -67,7 +67,7 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer { updatePosition(obj) //obj.onChildrenChange() - obj.propertyNameFlow.onEach { name -> + obj.onPropertyChange(updateScope) { name -> if ( name.startsWith(Solid.POSITION_KEY) || name.startsWith(Solid.ROTATION) || @@ -78,7 +78,7 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer { } else if (name == Vision.VISIBLE_KEY) { visible = obj.visible ?: true } - }.launchIn(updateScope) + } obj.structureChanges.onEach { (nameToken, _, child) -> // if (name.isEmpty()) { diff --git a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeReferenceFactory.kt b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeReferenceFactory.kt index 08866d46..8ccbc37f 100644 --- a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeReferenceFactory.kt +++ b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeReferenceFactory.kt @@ -9,8 +9,6 @@ import hep.dataforge.vision.solid.SolidReferenceGroup.Companion.REFERENCE_CHILD_ import info.laht.threekt.core.BufferGeometry import info.laht.threekt.core.Object3D import info.laht.threekt.objects.Mesh -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach import kotlin.reflect.KClass public object ThreeReferenceFactory : ThreeFactory { @@ -47,7 +45,7 @@ public object ThreeReferenceFactory : ThreeFactory { //TODO apply child properties - obj.propertyNameFlow.onEach { name-> + obj.onPropertyChange(three.updateScope) { name-> if (name.firstOrNull()?.body == REFERENCE_CHILD_PROPERTY_PREFIX) { val childName = name.firstOrNull()?.index?.toName() ?: error("Wrong syntax for reference child property: '$name'") val propertyName = name.cutFirst() @@ -57,7 +55,7 @@ public object ThreeReferenceFactory : ThreeFactory { } else { object3D.updateProperty(obj, name) } - }.launchIn(three.updateScope) + } return object3D From eefc036dcb7b3174eef304b7feaf98f5c8f1674a Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Mon, 21 Dec 2020 09:49:42 +0300 Subject: [PATCH 10/19] Remove `attachChildren`. Move group children to constructor --- .../kotlin/hep/dataforge/vision/Vision.kt | 1 + .../hep/dataforge/vision/VisionChange.kt | 7 +---- .../hep/dataforge/vision/VisionGroup.kt | 10 ------- .../hep/dataforge/vision/VisionGroupBase.kt | 19 ++++++++------ .../hep/dataforge/vision/VisionManager.kt | 9 ++----- .../hep/dataforge/vision/solid/SolidGroup.kt | 26 ++++--------------- .../dataforge/vision/solid/SolidManager.kt | 11 ++++---- .../dataforge/vision/solid/SolidReference.kt | 8 ------ .../dataforge/vision/solid/transform/UnRef.kt | 4 ++- 9 files changed, 28 insertions(+), 67 deletions(-) diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt index ba585b46..eda9e70b 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt @@ -62,6 +62,7 @@ public interface Vision : Described { * 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. */ + @OptIn(ExperimentalCoroutinesApi::class) public val propertyChanges: Flow get() = callbackFlow { coroutineScope { onPropertyChange(this) { diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionChange.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionChange.kt index 7dddd655..cbff844c 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionChange.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionChange.kt @@ -66,12 +66,7 @@ public class VisionChange( public val vision: Vision? = null, @Serializable(MetaSerializer::class) public val properties: Meta? = null, public val children: Map? = null, -) { - init { - (vision as? VisionGroup)?.attachChildren() - } - -} +) public inline fun VisionChange(manager: VisionManager, block: VisionChangeBuilder.() -> Unit): VisionChange = VisionChangeBuilder().apply(block).isolate(manager) diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionGroup.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionGroup.kt index 75855eb1..0f3967af 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionGroup.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionGroup.kt @@ -45,16 +45,6 @@ public interface VisionGroup : Provider, Vision, VisionContainer { } } - /** - * A fix for serialization bug that writes all proper parents inside the tree after deserialization - */ - public fun attachChildren() { - children.values.forEach { - it.parent = this - (it as? VisionGroup)?.attachChildren() - } - } - public companion object { public const val STYLE_TARGET: String = "style" } diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionGroupBase.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionGroupBase.kt index 0752b98b..6cc2b97b 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionGroupBase.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionGroupBase.kt @@ -11,23 +11,26 @@ import kotlinx.serialization.Transient /** * Abstract implementation of mutable group of [Vision] + * + * @param childrenInternal Internal mutable container for group children */ @Serializable @SerialName("vision.group") -public open class VisionGroupBase : VisionBase(), MutableVisionGroup { - - /** - * Internal mutable container for group children - * TODO made protected due to [https://github.com/Kotlin/kotlinx.serialization/issues/1200] - */ - @SerialName("children") - protected val childrenInternal: MutableMap = LinkedHashMap() +public open class VisionGroupBase( + @SerialName("children") internal val childrenInternal: MutableMap = LinkedHashMap(), +) : VisionBase(), MutableVisionGroup { /** * A map of top level named children */ override val children: Map get() = childrenInternal + init { + childrenInternal.values.forEach { + it.parent = this + } + } + override suspend fun notifyPropertyChanged(propertyName: Name) { super.notifyPropertyChanged(propertyName) for (obj in this) { diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionManager.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionManager.kt index eb34ed84..9a831799 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionManager.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionManager.kt @@ -30,16 +30,11 @@ public class VisionManager(meta: Meta) : AbstractPlugin(meta) { serializersModule = this@VisionManager.serializersModule } - public fun decodeFromString(string: String): Vision = jsonFormat.decodeFromString(visionSerializer, string).also { - (it as? VisionGroup)?.attachChildren() - } + public fun decodeFromString(string: String): Vision = jsonFormat.decodeFromString(visionSerializer, string) public fun encodeToString(vision: Vision): String = jsonFormat.encodeToString(visionSerializer, vision) - public fun decodeFromJson(json: JsonElement): Vision = - jsonFormat.decodeFromJsonElement(visionSerializer, json).also { - (it as? VisionGroup)?.attachChildren() - } + public fun decodeFromJson(json: JsonElement): Vision = jsonFormat.decodeFromJsonElement(visionSerializer, json) public fun encodeToJsonElement(vision: Vision): JsonElement = jsonFormat.encodeToJsonElement(visionSerializer, vision) diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt index c9d1654a..e2abc4e8 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt @@ -23,21 +23,17 @@ public interface PrototypeHolder { /** * Represents 3-dimensional Visual Group + * @param prototypes A container for templates visible inside this group */ @Serializable @SerialName("group.solid") -public class SolidGroup : VisionGroupBase(), Solid, PrototypeHolder { +public class SolidGroup( + @Serializable(Prototypes.Companion::class) @SerialName("prototypes") private var prototypes: MutableVisionGroup? = null, +) : VisionGroupBase(), Solid, PrototypeHolder { override val descriptor: NodeDescriptor get() = Solid.descriptor - /** - * A container for templates visible inside this group - */ - @Serializable(Prototypes.Companion::class) - @SerialName("prototypes") - internal var prototypes: MutableVisionGroup? = null - /** * Ger a prototype redirecting the request to the parent if prototype is not found */ @@ -60,13 +56,6 @@ public class SolidGroup : VisionGroupBase(), Solid, PrototypeHolder { override var scale: Point3D? = null - override fun attachChildren() { - prototypes?.parent = this - prototypes?.attachChildren() - super.attachChildren() - } - - // /** // * TODO add special static group to hold statics without propagation // */ @@ -108,16 +97,11 @@ public fun VisionContainerBuilder.group(name: String, action: SolidGroup @Serializable(Prototypes.Companion::class) internal class Prototypes( children: Map = emptyMap(), -) : VisionGroupBase(), PrototypeHolder { +) : VisionGroupBase(children as? MutableMap ?: children.toMutableMap()), PrototypeHolder { init { - childrenInternal.putAll(children) - } - - override fun attachChildren() { children.values.forEach { it.parent = parent - (it as? VisionGroup)?.attachChildren() } } diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidManager.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidManager.kt index 5f3c59bc..fa67fe5c 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidManager.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidManager.kt @@ -7,7 +7,10 @@ import hep.dataforge.context.PluginTag import hep.dataforge.meta.Meta import hep.dataforge.names.Name import hep.dataforge.names.toName -import hep.dataforge.vision.* +import hep.dataforge.vision.Vision +import hep.dataforge.vision.VisionBase +import hep.dataforge.vision.VisionGroupBase +import hep.dataforge.vision.VisionManager import hep.dataforge.vision.VisionManager.Companion.VISION_SERIALIZER_MODULE_TARGET import kotlinx.serialization.PolymorphicSerializer import kotlinx.serialization.json.Json @@ -67,10 +70,6 @@ public class SolidManager(meta: Meta) : AbstractPlugin(meta) { public fun encodeToString(solid: Solid): String = jsonForSolids.encodeToString(PolymorphicSerializer(Vision::class), solid) - fun decodeFromString(str: String): Solid = jsonForSolids.decodeFromString(PolymorphicSerializer(Solid::class), str).also { - if(it is VisionGroup){ - it.attachChildren() - } - } + public fun decodeFromString(str: String): Solid = jsonForSolids.decodeFromString(PolymorphicSerializer(Solid::class), str) } } diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidReference.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidReference.kt index 6430fc3d..63cd0672 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidReference.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidReference.kt @@ -82,10 +82,6 @@ public class SolidReferenceGroup( includeDefaults: Boolean, ): MetaItem<*>? = getRefProperty(name, inherit, includeStyles, includeDefaults) - override fun attachChildren() { - //do nothing - } - override val descriptor: NodeDescriptor get() = prototype.descriptor @@ -142,10 +138,6 @@ public class SolidReferenceGroup( TODO("Not yet implemented") } - override fun attachChildren() { - //do nothing - } - override val descriptor: NodeDescriptor get() = prototype.descriptor } diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/transform/UnRef.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/transform/UnRef.kt index cb0ac4e2..8bc81ff1 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/transform/UnRef.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/transform/UnRef.kt @@ -26,7 +26,9 @@ internal object UnRef : VisualTreeTransform() { } private fun MutableVisionGroup.unref(name: Name) { - (this as? SolidGroup)?.prototypes?.set(name, null) + (this as? SolidGroup)?.prototypes{ + set(name, null) + } children.filter { (it.value as? SolidReferenceGroup)?.templateName == name }.forEach { (key, value) -> val reference = value as SolidReferenceGroup val newChild = mergeChild(reference, reference.prototype) From 66ea23ad60ec8366660930d4ede13b1b009e65be Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Mon, 21 Dec 2020 21:45:29 +0300 Subject: [PATCH 11/19] Optimized materials allocation --- .../vision/solid/demo/VariableBox.kt | 14 +-- .../kotlin/hep/dataforge/vision/Vision.kt | 28 ++--- .../kotlin/hep/dataforge/vision/VisionBase.kt | 8 +- .../kotlin/hep/dataforge/vision/misc.kt | 16 ++- .../hep/dataforge/vision/solid/SolidGroup.kt | 4 +- .../dataforge/vision/solid/SolidReference.kt | 17 ++- .../vision/solid/three/MeshThreeFactory.kt | 69 ++++++------ .../vision/solid/three/ThreeFactory.kt | 5 +- .../vision/solid/three/ThreeLabelFactory.kt | 8 +- .../vision/solid/three/ThreeMaterials.kt | 101 ++++++++++++++++-- .../info/laht/threekt/materials/Material.kt | 2 +- 11 files changed, 181 insertions(+), 91 deletions(-) diff --git a/demo/spatial-showcase/src/jsMain/kotlin/hep/dataforge/vision/solid/demo/VariableBox.kt b/demo/spatial-showcase/src/jsMain/kotlin/hep/dataforge/vision/solid/demo/VariableBox.kt index 2910e0d3..4fbb0903 100644 --- a/demo/spatial-showcase/src/jsMain/kotlin/hep/dataforge/vision/solid/demo/VariableBox.kt +++ b/demo/spatial-showcase/src/jsMain/kotlin/hep/dataforge/vision/solid/demo/VariableBox.kt @@ -11,7 +11,6 @@ import hep.dataforge.vision.solid.* import hep.dataforge.vision.solid.Solid.Companion.GEOMETRY_KEY import hep.dataforge.vision.solid.SolidMaterial.Companion.MATERIAL_COLOR_KEY import hep.dataforge.vision.solid.three.* -import hep.dataforge.vision.solid.three.ThreeMaterials.getMaterial import info.laht.threekt.core.BufferGeometry import info.laht.threekt.core.Object3D import info.laht.threekt.geometries.BoxBufferGeometry @@ -31,8 +30,8 @@ internal class VariableBox(xSize: Number, ySize: Number, zSize: Number) : ThreeV scaleX = xSize scaleY = ySize scaleZ = zSize - getProperty(MeshThreeFactory.EDGES_ENABLED_KEY, false) - getProperty(MeshThreeFactory.WIREFRAME_ENABLED_KEY, false) +// getProperty(MeshThreeFactory.EDGES_ENABLED_KEY, inherit = false, includeStyles = false) +// getProperty(MeshThreeFactory.WIREFRAME_ENABLED_KEY, inherit = false, includeStyles = false) } override fun render(three: ThreePlugin): Object3D { @@ -44,9 +43,10 @@ internal class VariableBox(xSize: Number, ySize: Number, zSize: Number) : ThreeV //JS sometimes tries to pass Geometry as BufferGeometry @Suppress("USELESS_IS_CHECK") if (geometry !is BufferGeometry) error("BufferGeometry expected") - val mesh = Mesh(geometry, getMaterial(this@VariableBox, true)).apply { + val mesh = Mesh(geometry, ThreeMaterials.DEFAULT).apply { + updateMaterial(this@VariableBox) applyEdges(this@VariableBox) - applyWireFrame(this@VariableBox) + //applyWireFrame(this@VariableBox) //set position for mesh updatePosition(this@VariableBox) @@ -69,10 +69,10 @@ internal class VariableBox(xSize: Number, ySize: Number, zSize: Number) : ThreeV mesh.scale.set(newXSize, newYSize, newZSize) mesh.updateMatrix() } - name.startsWith(MeshThreeFactory.WIREFRAME_KEY) -> mesh.applyWireFrame(this@VariableBox) + //name.startsWith(MeshThreeFactory.WIREFRAME_KEY) -> mesh.applyWireFrame(this@VariableBox) name.startsWith(MeshThreeFactory.EDGES_KEY) -> mesh.applyEdges(this@VariableBox) name.startsWith(MATERIAL_COLOR_KEY) -> { - mesh.material = getMaterial(this, true) + mesh.updateMaterial(this) } else -> mesh.updateProperty(this@VariableBox, name) } diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt index eda9e70b..28f6b988 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt @@ -4,6 +4,7 @@ import hep.dataforge.meta.MetaItem import hep.dataforge.meta.MutableItemProvider import hep.dataforge.meta.descriptors.Described import hep.dataforge.meta.descriptors.NodeDescriptor +import hep.dataforge.meta.descriptors.get import hep.dataforge.names.Name import hep.dataforge.names.asName import hep.dataforge.names.toName @@ -45,8 +46,8 @@ public interface Vision : Described { */ public fun getProperty( name: Name, - inherit: Boolean? = null, - includeStyles: Boolean? = null, + inherit: Boolean = false, + includeStyles: Boolean = true, includeDefaults: Boolean = true, ): MetaItem<*>? @@ -63,14 +64,15 @@ public interface Vision : Described { * if it should include inherited properties etc. */ @OptIn(ExperimentalCoroutinesApi::class) - public val propertyChanges: Flow get() = callbackFlow { - coroutineScope { - onPropertyChange(this) { - send(it) + public val propertyChanges: Flow + get() = callbackFlow { + coroutineScope { + onPropertyChange(this) { + send(it) + } + awaitClose { cancel() } } - awaitClose { cancel() } } - } /** @@ -93,7 +95,7 @@ public interface Vision : Described { } } -public fun Vision.asyncNotifyPropertyChange(propertyName: Name){ +public fun Vision.asyncNotifyPropertyChange(propertyName: Name) { scope.launch { notifyPropertyChanged(propertyName) } @@ -120,8 +122,8 @@ public fun Vision.allProperties( ): MutableItemProvider = object : MutableItemProvider { override fun getItem(name: Name): MetaItem<*>? = getProperty( name, - inherit = inherit, - includeStyles = includeStyles, + inherit = inherit ?: (descriptor?.get(name)?.inherited != false), + includeStyles = includeStyles ?: (descriptor?.get(name)?.usesStyles == true), includeDefaults = includeDefaults ) @@ -133,8 +135,8 @@ public fun Vision.allProperties( */ public fun Vision.getProperty( key: String, - inherit: Boolean? = null, - includeStyles: Boolean? = null, + inherit: Boolean = false, + includeStyles: Boolean = true, includeDefaults: Boolean = true, ): MetaItem<*>? = getProperty(key.toName(), inherit, includeStyles, includeDefaults) diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt index 141366d1..fdd7feed 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt @@ -65,15 +65,15 @@ public open class VisionBase : Vision { override fun getProperty( name: Name, - inherit: Boolean?, - includeStyles: Boolean?, + inherit: Boolean, + includeStyles: Boolean, includeDefaults: Boolean, ): MetaItem<*>? = sequence { yield(getOwnProperty(name)) - if (includeStyles ?: descriptor?.get(name)?.usesStyles != false) { + if (includeStyles) { yieldAll(getStyleItems(name)) } - if (inherit ?: descriptor?.get(name)?.inherited == true) { + if (inherit) { yield(parent?.getProperty(name, inherit, includeStyles, includeDefaults)) } yield(descriptor?.get(name)?.defaultItem()) diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/misc.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/misc.kt index ef1882fe..047cd2fa 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/misc.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/misc.kt @@ -6,15 +6,13 @@ import hep.dataforge.values.asValue @DslMarker public annotation class VisionBuilder -public fun Sequence?>.merge(): MetaItem<*>? { - return when (val first = firstOrNull { it != null }) { - null -> null - is MetaItem.ValueItem -> first //fast search for first entry if it is value - is MetaItem.NodeItem -> { - //merge nodes if first encountered node is meta - val laminate: Laminate = Laminate(mapNotNull { it.node }.toList()) - MetaItem.NodeItem(laminate) - } +public fun Sequence?>.merge(): MetaItem<*>? = when (val first = firstOrNull { it != null }) { + null -> null + is MetaItem.ValueItem -> first //fast search for first entry if it is value + is MetaItem.NodeItem -> { + //merge nodes if first encountered node is meta + val laminate: Laminate = Laminate(mapNotNull { it.node }.toList()) + MetaItem.NodeItem(laminate) } } diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt index e2abc4e8..4ddfe548 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt @@ -109,8 +109,8 @@ internal class Prototypes( override fun getProperty( name: Name, - inherit: Boolean?, - includeStyles: Boolean?, + inherit: Boolean, + includeStyles: Boolean, includeDefaults: Boolean, ): MetaItem<*>? = null diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidReference.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidReference.kt index 63cd0672..d9fbdba0 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidReference.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidReference.kt @@ -2,7 +2,6 @@ package hep.dataforge.vision.solid import hep.dataforge.meta.* import hep.dataforge.meta.descriptors.NodeDescriptor -import hep.dataforge.meta.descriptors.get import hep.dataforge.names.* import hep.dataforge.vision.* import kotlinx.coroutines.CoroutineScope @@ -15,17 +14,17 @@ public interface SolidReference : Vision { private fun SolidReference.getRefProperty( name: Name, - inherit: Boolean?, - includeStyles: Boolean?, + inherit: Boolean, + includeStyles: Boolean, includeDefaults: Boolean, ): MetaItem<*>? { return sequence { yield(getOwnProperty(name)) - if (includeStyles ?: descriptor?.get(name)?.usesStyles != false) { + if (includeStyles) { yieldAll(getStyleItems(name)) } yield(prototype.getProperty(name, inherit, includeStyles, includeDefaults)) - if (inherit ?: descriptor?.get(name)?.inherited == true) { + if (inherit) { yield(parent?.getProperty(name, inherit)) } }.merge() @@ -77,8 +76,8 @@ public class SolidReferenceGroup( override fun getProperty( name: Name, - inherit: Boolean?, - includeStyles: Boolean?, + inherit: Boolean, + includeStyles: Boolean, includeDefaults: Boolean, ): MetaItem<*>? = getRefProperty(name, inherit, includeStyles, includeDefaults) @@ -108,8 +107,8 @@ public class SolidReferenceGroup( override fun getProperty( name: Name, - inherit: Boolean?, - includeStyles: Boolean?, + inherit: Boolean, + includeStyles: Boolean, includeDefaults: Boolean, ): MetaItem<*>? = getRefProperty(name, inherit, includeStyles, includeDefaults) diff --git a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/MeshThreeFactory.kt b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/MeshThreeFactory.kt index 813086fd..f04e7a1a 100644 --- a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/MeshThreeFactory.kt +++ b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/MeshThreeFactory.kt @@ -9,10 +9,8 @@ import hep.dataforge.names.startsWith import hep.dataforge.vision.solid.Solid import hep.dataforge.vision.solid.SolidMaterial import hep.dataforge.vision.solid.layer -import hep.dataforge.vision.solid.three.ThreeMaterials.getMaterial 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 @@ -36,7 +34,7 @@ public abstract class MeshThreeFactory( //val meshMeta: Meta = obj.properties[Material3D.MATERIAL_KEY]?.node ?: Meta.empty - val mesh = Mesh(geometry, null).apply { + val mesh = Mesh(geometry, ThreeMaterials.DEFAULT).apply { matrixAutoUpdate = false //set position for mesh updatePosition(obj) @@ -49,11 +47,11 @@ public abstract class MeshThreeFactory( val oldGeometry = mesh.geometry as BufferGeometry val newGeometry = buildGeometry(obj) oldGeometry.attributes = newGeometry.attributes - mesh.applyWireFrame(obj) + //mesh.applyWireFrame(obj) mesh.applyEdges(obj) newGeometry.dispose() } - name.startsWith(WIREFRAME_KEY) -> mesh.applyWireFrame(obj) + //name.startsWith(WIREFRAME_KEY) -> mesh.applyWireFrame(obj) name.startsWith(EDGES_KEY) -> mesh.applyEdges(obj) else -> mesh.updateProperty(obj, name) } @@ -64,19 +62,19 @@ public abstract class MeshThreeFactory( public companion object { public val EDGES_KEY: Name = "edges".asName() - public val WIREFRAME_KEY: Name = "wireframe".asName() + //public val WIREFRAME_KEY: Name = "wireframe".asName() public val ENABLED_KEY: Name = "enabled".asName() public val EDGES_ENABLED_KEY: Name = EDGES_KEY + ENABLED_KEY public val EDGES_MATERIAL_KEY: Name = EDGES_KEY + SolidMaterial.MATERIAL_KEY - public val WIREFRAME_ENABLED_KEY: Name = WIREFRAME_KEY + ENABLED_KEY - public val WIREFRAME_MATERIAL_KEY: Name = WIREFRAME_KEY + SolidMaterial.MATERIAL_KEY + //public val WIREFRAME_ENABLED_KEY: Name = WIREFRAME_KEY + ENABLED_KEY + //public val WIREFRAME_MATERIAL_KEY: Name = WIREFRAME_KEY + SolidMaterial.MATERIAL_KEY } } internal fun Mesh.applyProperties(obj: Solid): Mesh = apply { - material = getMaterial(obj, true) + updateMaterial(obj) applyEdges(obj) - applyWireFrame(obj) + //applyWireFrame(obj) layers.enable(obj.layer) children.forEach { it.layers.enable(obj.layer) @@ -86,9 +84,16 @@ internal fun Mesh.applyProperties(obj: Solid): Mesh = apply { public fun Mesh.applyEdges(obj: Solid) { val edges = children.find { it.name == "@edges" } as? LineSegments //inherited edges definition, enabled by default - if (obj.getProperty(MeshThreeFactory.EDGES_ENABLED_KEY).boolean != false) { + if (obj.getProperty(MeshThreeFactory.EDGES_ENABLED_KEY, inherit = true, includeStyles = true).boolean != false) { val bufferGeometry = geometry as? BufferGeometry ?: return - val material = ThreeMaterials.getLineMaterial(obj.getProperty(MeshThreeFactory.EDGES_MATERIAL_KEY).node, true) + val material = ThreeMaterials.getLineMaterial( + obj.getProperty( + MeshThreeFactory.EDGES_MATERIAL_KEY, + inherit = true, + includeStyles = true + ).node, + true + ) if (edges == null) { add( LineSegments( @@ -109,23 +114,23 @@ public fun Mesh.applyEdges(obj: Solid) { } } -public fun Mesh.applyWireFrame(obj: Solid) { - children.find { it.name == "@wireframe" }?.let { - remove(it) - (it as LineSegments).dispose() - } - //inherited wireframe definition, disabled by default - if (obj.getProperty(MeshThreeFactory.WIREFRAME_ENABLED_KEY).boolean == true) { - val bufferGeometry = geometry as? BufferGeometry ?: return - val material = - ThreeMaterials.getLineMaterial(obj.getProperty(MeshThreeFactory.WIREFRAME_MATERIAL_KEY).node, true) - add( - LineSegments( - WireframeGeometry(bufferGeometry), - material - ).apply { - name = "@wireframe" - } - ) - } -} \ No newline at end of file +//public fun Mesh.applyWireFrame(obj: Solid) { +// children.find { it.name == "@wireframe" }?.let { +// remove(it) +// (it as LineSegments).dispose() +// } +// //inherited wireframe definition, disabled by default +// if (obj.getProperty(MeshThreeFactory.WIREFRAME_ENABLED_KEY).boolean == true) { +// val bufferGeometry = geometry as? BufferGeometry ?: return +// val material = +// ThreeMaterials.getLineMaterial(obj.getProperty(MeshThreeFactory.WIREFRAME_MATERIAL_KEY).node, true) +// add( +// LineSegments( +// WireframeGeometry(bufferGeometry), +// material +// ).apply { +// name = "@wireframe" +// } +// ) +// } +//} \ No newline at end of file diff --git a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeFactory.kt b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeFactory.kt index d6be69e1..30fa1422 100644 --- a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeFactory.kt +++ b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeFactory.kt @@ -7,7 +7,6 @@ import hep.dataforge.vision.Vision import hep.dataforge.vision.solid.* import hep.dataforge.vision.solid.SolidMaterial.Companion.MATERIAL_KEY import hep.dataforge.vision.solid.three.ThreeFactory.Companion.TYPE -import hep.dataforge.vision.solid.three.ThreeMaterials.getMaterial import hep.dataforge.vision.visible import info.laht.threekt.core.BufferGeometry import info.laht.threekt.core.Object3D @@ -34,7 +33,7 @@ public interface ThreeFactory { */ public fun Object3D.updatePosition(obj: Vision) { visible = obj.visible ?: true - if(obj is Solid) { + if (obj is Solid) { position.set(obj.x, obj.y, obj.z) setRotationFromEuler(obj.euler) scale.set(obj.scaleX, obj.scaleY, obj.scaleZ) @@ -47,7 +46,7 @@ public fun Object3D.updatePosition(obj: Vision) { */ public fun Object3D.updateProperty(source: Vision, propertyName: Name) { if (this is Mesh && propertyName.startsWith(MATERIAL_KEY)) { - this.material = getMaterial(source, true) + updateMaterialProperty(source, propertyName) } else if ( propertyName.startsWith(Solid.POSITION_KEY) || propertyName.startsWith(Solid.ROTATION) diff --git a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeLabelFactory.kt b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeLabelFactory.kt index 258628bb..8809ea2d 100644 --- a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeLabelFactory.kt +++ b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeLabelFactory.kt @@ -3,7 +3,6 @@ package hep.dataforge.vision.solid.three import hep.dataforge.context.logger import hep.dataforge.vision.solid.SolidLabel -import hep.dataforge.vision.solid.three.ThreeMaterials.getMaterial import info.laht.threekt.core.Object3D import info.laht.threekt.geometries.TextBufferGeometry import info.laht.threekt.objects.Mesh @@ -23,11 +22,12 @@ public object ThreeLabelFactory : ThreeFactory { height = 1 curveSegments = 1 }) - return Mesh(textGeo, getMaterial(obj, true)).apply { + return Mesh(textGeo, ThreeMaterials.DEFAULT).apply { + updateMaterial(obj) updatePosition(obj) - obj.onPropertyChange(three.updateScope){ _ -> + obj.onPropertyChange(three.updateScope) { _ -> //TODO - three.logger.warn{"Label parameter change not implemented"} + three.logger.warn { "Label parameter change not implemented" } } } } diff --git a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeMaterials.kt b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeMaterials.kt index 1736ce82..e9471af8 100644 --- a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeMaterials.kt +++ b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeMaterials.kt @@ -1,23 +1,28 @@ package hep.dataforge.vision.solid.three import hep.dataforge.meta.* +import hep.dataforge.names.Name import hep.dataforge.values.ValueType import hep.dataforge.values.int import hep.dataforge.values.string import hep.dataforge.vision.Colors import hep.dataforge.vision.Vision +import hep.dataforge.vision.allStyles +import hep.dataforge.vision.merge import hep.dataforge.vision.solid.SolidMaterial import info.laht.threekt.materials.LineBasicMaterial import info.laht.threekt.materials.Material import info.laht.threekt.materials.MeshBasicMaterial import info.laht.threekt.materials.MeshPhongMaterial import info.laht.threekt.math.Color +import info.laht.threekt.objects.Mesh public object ThreeMaterials { public val DEFAULT_COLOR: Color = Color(Colors.darkgreen) public val DEFAULT: MeshBasicMaterial = MeshBasicMaterial().apply { color.set(DEFAULT_COLOR) + cached = true } public val DEFAULT_LINE_COLOR: Color = Color(Colors.black) public val DEFAULT_LINE: LineBasicMaterial = LineBasicMaterial().apply { @@ -54,7 +59,7 @@ public object ThreeMaterials { private val materialCache = HashMap() - private fun buildMaterial(meta: Meta): Material { + internal fun buildMaterial(meta: Meta): Material { return if (meta[SolidMaterial.SPECULAR_COLOR_KEY] != null) { MeshPhongMaterial().apply { color = meta[SolidMaterial.COLOR_KEY]?.getColor() ?: DEFAULT_COLOR @@ -79,15 +84,26 @@ public object ThreeMaterials { } } - public fun getMaterial(vision3D: Vision, cache: Boolean): Material { - val meta = vision3D.getProperty(SolidMaterial.MATERIAL_KEY, inherit = true).node ?: return DEFAULT - return if (cache) { - materialCache.getOrPut(meta) { buildMaterial(meta) } - } else { - buildMaterial(meta) + internal fun cacheMeta(meta: Meta): Material = materialCache.getOrPut(meta) { + buildMaterial(meta).apply { + cached = true } } + +// internal fun getMaterial(vision: Vision, cache: Boolean): Material { +// val meta = vision.getProperty(SolidMaterial.MATERIAL_KEY, inherit = true).node ?: return DEFAULT +// return if (cache) { +// materialCache.getOrPut(meta) { +// buildMaterial(meta).apply { +// cached = true +// } +// } +// } else { +// buildMaterial(meta) +// } +// } + } /** @@ -111,3 +127,74 @@ public fun MetaItem<*>.getColor(): Color { } } +private var Material.cached: Boolean + get() = userData["cached"] == true + set(value) { + userData["cached"] = value + } + +public fun Mesh.updateMaterial(vision: Vision) { + //val meta = vision.getProperty(SolidMaterial.MATERIAL_KEY, inherit = true).node + val ownMaterialMeta = vision.getOwnProperty(SolidMaterial.MATERIAL_KEY) + val stylesMaterialMeta = vision.allStyles[SolidMaterial.MATERIAL_KEY] + val parentMaterialMeta = vision.parent?.getProperty( + SolidMaterial.MATERIAL_KEY, + inherit = true, + includeStyles = false, + includeDefaults = false + ) + material = when { + ownMaterialMeta == null && stylesMaterialMeta == null && parentMaterialMeta == null -> { + //use default is not material properties are defined + ThreeMaterials.DEFAULT + } + ownMaterialMeta == null && parentMaterialMeta == null -> { + //If material is style-based, use cached + ThreeMaterials.cacheMeta(stylesMaterialMeta.node ?: Meta.EMPTY) + } + else -> { + val merge = sequenceOf(ownMaterialMeta, stylesMaterialMeta, parentMaterialMeta).merge().node ?: Meta.EMPTY + ThreeMaterials.buildMaterial(merge) + } + } +} + +public fun Mesh.updateMaterialProperty(vision: Vision, propertyName: Name) { + if (material.cached) { + //generate a new material since cached material should not be changed + updateMaterial(vision) + } else { + when (propertyName) { + SolidMaterial.MATERIAL_COLOR_KEY -> { + material.asDynamic().color = vision.getProperty( + SolidMaterial.MATERIAL_COLOR_KEY, + inherit = true, + includeStyles = true, + includeDefaults = false + )?.getColor() ?: ThreeMaterials.DEFAULT_COLOR + material.needsUpdate = true + } + SolidMaterial.MATERIAL_OPACITY_KEY -> { + val opacity = vision.getProperty( + SolidMaterial.MATERIAL_OPACITY_KEY, + inherit = true, + includeStyles = true, + includeDefaults = false + ).double ?: 1.0 + material.asDynamic().opacity = opacity + material.transparent = opacity < 1.0 + material.needsUpdate = true + } + SolidMaterial.MATERIAL_WIREFRAME_KEY -> { + material.asDynamic().wireframe = vision.getProperty( + SolidMaterial.MATERIAL_WIREFRAME_KEY, + inherit = true, + includeStyles = true, + includeDefaults = false + ).boolean ?: false + material.needsUpdate = true + } + else -> console.warn("Unrecognized material property: $propertyName") + } + } +} \ No newline at end of file diff --git a/visionforge-threejs/src/main/kotlin/info/laht/threekt/materials/Material.kt b/visionforge-threejs/src/main/kotlin/info/laht/threekt/materials/Material.kt index 3950c963..9a2b72d0 100644 --- a/visionforge-threejs/src/main/kotlin/info/laht/threekt/materials/Material.kt +++ b/visionforge-threejs/src/main/kotlin/info/laht/threekt/materials/Material.kt @@ -111,7 +111,7 @@ open external class Material { var visible: Boolean - var userData: Map + var userData: dynamic /** * Specifies that the material needs to be updated at the WebGL level. Set it to true if you made changes that need to be reflected in WebGL. From 216be4a6a18dfedd348a991bd2ed48433825522f Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 22 Dec 2020 13:33:05 +0300 Subject: [PATCH 12/19] Handle property clearing in updates. --- .../main/kotlin/ru/mipt/npm/sat/satServer.kt | 3 +- .../kotlin/hep/dataforge/vision/Vision.kt | 5 +- .../kotlin/hep/dataforge/vision/VisionBase.kt | 30 +++++++++-- .../hep/dataforge/vision/VisionChange.kt | 8 ++- .../vision/three/server/VisionServer.kt | 8 --- .../dataforge/vision/solid/ColorAccessor.kt | 51 +++++++++++++++++++ .../dataforge/vision/solid/SolidMaterial.kt | 38 -------------- 7 files changed, 90 insertions(+), 53 deletions(-) create mode 100644 visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/ColorAccessor.kt diff --git a/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/satServer.kt b/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/satServer.kt index 761cd982..a605e717 100644 --- a/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/satServer.kt +++ b/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/satServer.kt @@ -3,6 +3,7 @@ package ru.mipt.npm.sat import hep.dataforge.names.toName import hep.dataforge.vision.solid.Solid +import hep.dataforge.vision.solid.clear import hep.dataforge.vision.solid.color import hep.dataforge.vision.solid.invoke import hep.dataforge.vision.three.server.* @@ -42,7 +43,7 @@ fun main() { val targetVision = sat[target] as Solid targetVision.color("red") delay(300) - targetVision.color("darkgreen") + targetVision.color.clear() delay(10) } } diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt index 28f6b988..39d5ae5a 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt @@ -57,6 +57,9 @@ public interface Vision : Described { */ 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) /** @@ -81,7 +84,7 @@ public interface Vision : Described { public suspend fun notifyPropertyChanged(propertyName: Name): Unit /** - * Update this vision using external meta. Children are not updated. + * Update this vision using a dif represented by [VisionChange]. */ public fun update(change: VisionChange) diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt index fdd7feed..a70cdf21 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt @@ -3,12 +3,14 @@ package hep.dataforge.vision import hep.dataforge.meta.Config import hep.dataforge.meta.MetaItem import hep.dataforge.meta.MutableMeta +import hep.dataforge.meta.asMetaItem import hep.dataforge.meta.descriptors.NodeDescriptor import hep.dataforge.meta.descriptors.defaultItem import hep.dataforge.meta.descriptors.get -import hep.dataforge.meta.update import hep.dataforge.names.Name import hep.dataforge.names.asName +import hep.dataforge.names.plus +import hep.dataforge.values.Null import hep.dataforge.values.ValueType import hep.dataforge.vision.Vision.Companion.STYLE_KEY import kotlinx.coroutines.CoroutineScope @@ -27,6 +29,7 @@ internal data class PropertyListener( val action: (name: Name) -> Unit, ) + @Serializable @SerialName("vision") public open class VisionBase : Vision { @@ -100,6 +103,8 @@ public open class VisionBase : Vision { } } + + //TODO check memory consumption for the flow @Transient private val propertyInvalidationFlow: MutableSharedFlow = MutableSharedFlow() @@ -116,13 +121,30 @@ public open class VisionBase : Vision { propertyInvalidationFlow.emit(propertyName) } - public fun configure(block: MutableMeta<*>.() -> Unit) { - getOrCreateConfig().block() + public fun configure(block: suspend MutableMeta<*>.() -> Unit) { + scope.launch { + getOrCreateConfig().block() + } } override fun update(change: VisionChange) { + + fun updateProperties(at: Name, item: MetaItem<*>) { + when (item) { + is MetaItem.ValueItem -> { + if (item.value == Null) { + setProperty(at, null) + } else + setProperty(at, item) + } + is MetaItem.NodeItem -> item.node.items.forEach { (token, childItem) -> + updateProperties(at + token, childItem) + } + } + } + change.properties?.let { - getOrCreateConfig().update(it) + updateProperties(Name.EMPTY, it.asMetaItem()) } } diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionChange.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionChange.kt index cbff844c..c0cc594a 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionChange.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionChange.kt @@ -3,6 +3,7 @@ package hep.dataforge.vision import hep.dataforge.meta.* import hep.dataforge.names.Name import hep.dataforge.names.plus +import hep.dataforge.values.Null import kotlinx.coroutines.* import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow @@ -30,7 +31,8 @@ public class VisionChangeBuilder : VisionContainerBuilder { public fun propertyChanged(visionName: Name, propertyName: Name, item: MetaItem<*>?) { if (visionName == Name.EMPTY) { - propertyChange[propertyName] = item + //Write property removal as [Null] + propertyChange[propertyName] = (item ?: Null.asMetaItem()) } else { getOrPutChild(visionName).propertyChanged(Name.EMPTY, propertyName, item) } @@ -112,6 +114,10 @@ public fun Vision.flowChanges( coroutineScope { collectChange(Name.EMPTY, this@flowChanges) { collector } + //Send initial vision state + val initialChange = VisionChange(vision = isolate(manager)) + emit(initialChange) + while (currentCoroutineContext().isActive) { //Wait for changes to accumulate delay(collectionDuration) diff --git a/visionforge-server/src/main/kotlin/hep/dataforge/vision/three/server/VisionServer.kt b/visionforge-server/src/main/kotlin/hep/dataforge/vision/three/server/VisionServer.kt index 13528ff7..5baf2643 100644 --- a/visionforge-server/src/main/kotlin/hep/dataforge/vision/three/server/VisionServer.kt +++ b/visionforge-server/src/main/kotlin/hep/dataforge/vision/three/server/VisionServer.kt @@ -134,14 +134,6 @@ public class VisionServer internal constructor( try { withContext(visionManager.context.coroutineContext) { - - val initialVision = VisionChange(vision = vision) - val initialJson = visionManager.jsonFormat.encodeToString( - VisionChange.serializer(), - initialVision - ) - outgoing.send(Frame.Text(initialJson)) - vision.flowChanges(visionManager, updateInterval.milliseconds).collect { update -> val json = visionManager.jsonFormat.encodeToString( VisionChange.serializer(), diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/ColorAccessor.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/ColorAccessor.kt new file mode 100644 index 00000000..3b78d7b5 --- /dev/null +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/ColorAccessor.kt @@ -0,0 +1,51 @@ +package hep.dataforge.vision.solid + +import hep.dataforge.meta.MutableItemProvider +import hep.dataforge.meta.set +import hep.dataforge.meta.value +import hep.dataforge.names.Name +import hep.dataforge.values.Value +import hep.dataforge.values.asValue +import hep.dataforge.values.string +import hep.dataforge.vision.Colors +import hep.dataforge.vision.VisionBuilder + +@VisionBuilder +public class ColorAccessor(private val parent: MutableItemProvider, private val colorKey: Name) { + public var value: Value? + get() = parent.getItem(colorKey).value + set(value) { + parent[colorKey] = value + } +} + +public var ColorAccessor?.string: String? + get() = this?.value?.string + set(value) { + this?.value = value?.asValue() + } + +/** + * Set [webcolor](https://en.wikipedia.org/wiki/Web_colors) as string + */ +public operator fun ColorAccessor?.invoke(webColor: String) { + this?.value = webColor.asValue() +} + +/** + * Set color as RGB integer + */ +public operator fun ColorAccessor?.invoke(rgb: Int) { + this?.value = Colors.rgbToString(rgb).asValue() +} + +/** + * Set color as RGB + */ +public operator fun ColorAccessor?.invoke(r: UByte, g: UByte, b: UByte) { + this?.value = Colors.rgbToString(r, g, b).asValue() +} + +public fun ColorAccessor?.clear(){ + this?.value = null +} \ No newline at end of file diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidMaterial.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidMaterial.kt index d845df8b..2394030b 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidMaterial.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidMaterial.kt @@ -6,51 +6,13 @@ import hep.dataforge.meta.descriptors.attributes import hep.dataforge.names.Name import hep.dataforge.names.asName import hep.dataforge.names.plus -import hep.dataforge.values.Value import hep.dataforge.values.ValueType import hep.dataforge.values.asValue -import hep.dataforge.values.string import hep.dataforge.vision.* import hep.dataforge.vision.solid.SolidMaterial.Companion.MATERIAL_COLOR_KEY import hep.dataforge.vision.solid.SolidMaterial.Companion.MATERIAL_KEY import hep.dataforge.vision.solid.SolidMaterial.Companion.MATERIAL_OPACITY_KEY -@VisionBuilder -public class ColorAccessor(private val parent: MutableItemProvider, private val colorKey: Name) { - public var value: Value? - get() = parent.getItem(colorKey).value - set(value) { - parent[colorKey] = value - } -} - -public var ColorAccessor?.string: String? - get() = this?.value?.string - set(value) { - this?.value = value?.asValue() - } - -/** - * Set [webcolor](https://en.wikipedia.org/wiki/Web_colors) as string - */ -public operator fun ColorAccessor?.invoke(webColor: String) { - this?.value = webColor.asValue() -} - -/** - * Set color as RGB integer - */ -public operator fun ColorAccessor?.invoke(rgb: Int) { - this?.value = Colors.rgbToString(rgb).asValue() -} - -/** - * Set color as RGB - */ -public operator fun ColorAccessor?.invoke(r: UByte, g: UByte, b: UByte) { - this?.value = Colors.rgbToString(r, g, b).asValue() -} - @VisionBuilder public class SolidMaterial : Scheme() { From 3c0df98f501c22c1ff4bcd66152a5ee870f65deb Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 22 Dec 2020 17:48:14 +0300 Subject: [PATCH 13/19] Strict mode for Three canvas. Styles builders --- .../main/kotlin/ru/mipt/npm/sat/geometry.kt | 27 +++++++++++---- .../main/kotlin/ru/mipt/npm/sat/satServer.kt | 4 +-- .../vision/react/ThreeCanvasComponent.kt | 3 +- .../hep/dataforge/vision/StyleReference.kt | 33 +++++++++++++++++++ .../kotlin/hep/dataforge/vision/StyleSheet.kt | 4 ++- .../hep/dataforge/vision/VisionChange.kt | 2 +- .../vision/VisionPropertyContainer.kt | 18 ++++++++++ .../dataforge/vision/client/VisionClient.kt | 2 +- .../hep/dataforge/vision/solid/SolidGroup.kt | 1 + .../vision/solid/three/ThreeCanvas.kt | 9 +++-- .../vision/solid/three/ThreePlugin.kt | 16 +++++++-- 11 files changed, 99 insertions(+), 20 deletions(-) create mode 100644 visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/StyleReference.kt create mode 100644 visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionPropertyContainer.kt diff --git a/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/geometry.kt b/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/geometry.kt index c9c1512f..283d31ef 100644 --- a/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/geometry.kt +++ b/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/geometry.kt @@ -1,6 +1,9 @@ package ru.mipt.npm.sat +import hep.dataforge.meta.set import hep.dataforge.vision.solid.* +import hep.dataforge.vision.style +import hep.dataforge.vision.useStyle import kotlin.math.PI internal fun visionOfSatellite( @@ -12,7 +15,18 @@ internal fun visionOfSatellite( ySegmentSize: Number = xSegmentSize, fiberDiameter: Number = 1.0, ): SolidGroup = SolidGroup { - opacity = 0.3 + val transparent by style { + this[SolidMaterial.MATERIAL_OPACITY_KEY] = 0.3 + } + + val red by style { + this[SolidMaterial.MATERIAL_COLOR_KEY] = "red" + } + + val blue by style { + this[SolidMaterial.MATERIAL_COLOR_KEY] = "blue" + } + val totalXSize = xSegments * xSegmentSize.toDouble() val totalYSize = ySegments * ySegmentSize.toDouble() for (layer in 1..layers) { @@ -20,6 +34,7 @@ internal fun visionOfSatellite( for (i in 1..xSegments) { for (j in 1..ySegments) { box(xSegmentSize, ySegmentSize, layerHeight, name = "segment[$i,$j]") { + useStyle(transparent) z = (layer - 0.5) * layerHeight.toDouble() x = (i - 0.5) * xSegmentSize.toDouble() y = (j - 0.5) * ySegmentSize.toDouble() @@ -29,23 +44,21 @@ internal fun visionOfSatellite( group("fibers") { for (i in 1..xSegments) { cylinder(fiberDiameter, totalYSize) { + useStyle(red) rotationX = PI / 2 z = (layer - 1.0) * layerHeight.toDouble() + fiberDiameter.toDouble() x = (i - 0.5) * xSegmentSize.toDouble() - y = totalYSize/2 - - color("red") + y = totalYSize / 2 } } for (j in 1..ySegments) { cylinder(fiberDiameter, totalXSize) { + useStyle(blue) rotationY = PI / 2 z = (layer) * layerHeight.toDouble() - fiberDiameter.toDouble() y = (j - 0.5) * xSegmentSize.toDouble() - x = totalXSize/2 - - color("blue") + x = totalXSize / 2 } } } diff --git a/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/satServer.kt b/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/satServer.kt index a605e717..a1f0dca1 100644 --- a/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/satServer.kt +++ b/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/satServer.kt @@ -42,9 +42,9 @@ fun main() { val target = "layer[$randomLayer].segment[$randomI,$randomJ]".toName() val targetVision = sat[target] as Solid targetVision.color("red") - delay(300) + delay(1000) targetVision.color.clear() - delay(10) + delay(500) } } diff --git a/ui/react/src/main/kotlin/hep/dataforge/vision/react/ThreeCanvasComponent.kt b/ui/react/src/main/kotlin/hep/dataforge/vision/react/ThreeCanvasComponent.kt index aa78a0fb..877ce5bc 100644 --- a/ui/react/src/main/kotlin/hep/dataforge/vision/react/ThreeCanvasComponent.kt +++ b/ui/react/src/main/kotlin/hep/dataforge/vision/react/ThreeCanvasComponent.kt @@ -37,8 +37,7 @@ public val ThreeCanvasComponent: FunctionalComponent = functio if (canvas == null) { val element = elementRef.current as? HTMLElement ?: error("Canvas element not found") val three: ThreePlugin = props.context.plugins.fetch(ThreePlugin) - val newCanvas: ThreeCanvas = - three.createCanvas(element, props.options ?: Canvas3DOptions.empty()) + val newCanvas: ThreeCanvas = three.createCanvas(element, props.options ?: Canvas3DOptions.empty()) props.canvasCallback?.invoke(newCanvas) canvas = newCanvas } diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/StyleReference.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/StyleReference.kt new file mode 100644 index 00000000..ac8da104 --- /dev/null +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/StyleReference.kt @@ -0,0 +1,33 @@ +package hep.dataforge.vision + +import hep.dataforge.meta.DFExperimental +import hep.dataforge.meta.Meta +import hep.dataforge.meta.MetaBuilder +import kotlin.properties.ReadOnlyProperty + +/** + * A reference to a style defined in a specific container + */ +public class StyleReference(public val owner: VisionGroup, public val name: String) + +private tailrec fun styleIsDefined(vision: Vision, reference: StyleReference): Boolean = when { + reference.owner === vision -> true + vision.parent == null -> false + else -> styleIsDefined(vision.parent!!, reference) +} + +@VisionBuilder +public fun Vision.useStyle(reference: StyleReference) { + //check that style is defined in a parent + //check(styleIsDefined(this, reference)) { "Style reference does not belong to a Vision parent" } + useStyle(reference.name) +} + +@DFExperimental +@VisionBuilder +public fun VisionGroup.style(builder: MetaBuilder.() -> Unit): ReadOnlyProperty = + ReadOnlyProperty { _, property -> + val styleName = property.name + styleSheet.define(styleName, Meta(builder)) + StyleReference(this, styleName) + } \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/StyleSheet.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/StyleSheet.kt index d90fe655..a22ea6bb 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/StyleSheet.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/StyleSheet.kt @@ -110,4 +110,6 @@ public fun Vision.getStyleItems(name: Name): Sequence> { /** * Collect all styles for this object in a single laminate */ -public val Vision.allStyles: Laminate get() = Laminate(styles.mapNotNull(::getStyle)) \ No newline at end of file +public val Vision.allStyles: Laminate get() = Laminate(styles.mapNotNull(::getStyle)) + + diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionChange.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionChange.kt index c0cc594a..2bfde782 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionChange.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionChange.kt @@ -63,7 +63,7 @@ private fun Vision.isolate(manager: VisionManager): Vision { } @Serializable -public class VisionChange( +public data class VisionChange( public val reset: Boolean = false, public val vision: Vision? = null, @Serializable(MetaSerializer::class) public val properties: Meta? = null, diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionPropertyContainer.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionPropertyContainer.kt new file mode 100644 index 00000000..f206f063 --- /dev/null +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionPropertyContainer.kt @@ -0,0 +1,18 @@ +package hep.dataforge.vision + +import hep.dataforge.meta.MetaItem +import hep.dataforge.names.Name + +/** + * Property containers are used to create a symmetric behaviors for vision properties and style builders + */ +public interface VisionPropertyContainer { + public fun getProperty( + name: Name, + inherit: Boolean = false, + includeStyles: Boolean = true, + includeDefaults: Boolean = true, + ): MetaItem<*>? + + public fun setProperty(name: Name, item: MetaItem<*>?, notify: Boolean = true) +} \ No newline at end of file diff --git a/visionforge-core/src/jsMain/kotlin/hep/dataforge/vision/client/VisionClient.kt b/visionforge-core/src/jsMain/kotlin/hep/dataforge/vision/client/VisionClient.kt index 36f0ee1f..11eaa0e1 100644 --- a/visionforge-core/src/jsMain/kotlin/hep/dataforge/vision/client/VisionClient.kt +++ b/visionforge-core/src/jsMain/kotlin/hep/dataforge/vision/client/VisionClient.kt @@ -138,7 +138,7 @@ public class VisionClient : AbstractPlugin() { renderVision(element, change.vision, outputMeta) } - logger.debug { "Got update $change for output with name $name" } + logger.info { "Got update ${change.toString()} for output with name $name" } visionMap[element]?.update(change) ?: console.info("Target vision for element $element with name $name not found") } else { diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt index 4ddfe548..b64074da 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt @@ -88,6 +88,7 @@ public fun VisionContainerBuilder.group( /** * Define a group with given [name], attach it to this parent and return it. */ +@VisionBuilder public fun VisionContainerBuilder.group(name: String, action: SolidGroup.() -> Unit = {}): SolidGroup = SolidGroup().apply(action).also { set(name, it) } diff --git a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeCanvas.kt b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeCanvas.kt index 11fffe88..f5b679e5 100644 --- a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeCanvas.kt +++ b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeCanvas.kt @@ -130,7 +130,8 @@ public class ThreeCanvas( } } - public fun attach(element: Element) { + internal fun attach(element: Element) { + check(element.children.length == 0){"The element for Three canvas is not empty"} element.appendChild(canvas) updateSize() } @@ -189,8 +190,10 @@ public class ThreeCanvas( } public override fun render(vision: Solid) { - //clear old root - clear() + scene.children.find { it.name == "@root" }?.let { + //Throw error is something is already rendered here + error("Root object already is present in the canvas") + } val object3D = three.buildObject3D(vision) object3D.name = "@root" diff --git a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreePlugin.kt b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreePlugin.kt index a7a74ce3..eaadc56a 100644 --- a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreePlugin.kt +++ b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreePlugin.kt @@ -136,9 +136,19 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer { return if (vision is Solid) ElementVisionRenderer.DEFAULT_RATING else ElementVisionRenderer.ZERO_RATING } + public fun renderSolid( + element: Element, + vision: Solid, + options: Canvas3DOptions, + ): ThreeCanvas = createCanvas(element, options).apply { + render(vision) + } + override fun render(element: Element, vision: Vision, meta: Meta) { - createCanvas(element, Canvas3DOptions.read(meta)).render( - vision as? Solid ?: error("Solid expected but ${vision::class} is found") + renderSolid( + element, + vision as? Solid ?: error("Solid expected but ${vision::class} is found"), + Canvas3DOptions.read(meta) ) } @@ -153,7 +163,7 @@ public fun ThreePlugin.render( element: HTMLElement, obj: Solid, options: Canvas3DOptions.() -> Unit = {}, -): ThreeCanvas = createCanvas(element, Canvas3DOptions(options)).apply { render(obj) } +): ThreeCanvas = renderSolid(element, obj, Canvas3DOptions(options)) internal operator fun Object3D.set(token: NameToken, object3D: Object3D) { object3D.name = token.toString() From 25a47a971942e6fadfacd88ec2c6b83c35955802 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 25 Dec 2020 15:28:33 +0300 Subject: [PATCH 14/19] Migrate to DF 0.3 --- build.gradle.kts | 4 +-- .../dataforge/vision/gdml/demo/readFile.kt | 1 - .../hep/dataforge/vision/solid/demo/demo.kt | 10 ++++-- .../vision/solid/demo/VariableBox.kt | 8 +---- .../hep/dataforge/vision/react/MetaViewer.kt | 7 ++-- .../dataforge/vision/react/PropertyEditor.kt | 8 +++-- .../hep/dataforge/vision/react/TreeStyles.kt | 20 +++++------ .../dataforge/vision/react/valueChooser.kt | 4 +-- .../kotlin/hep/dataforge/vision/Colors.kt | 10 +++--- .../kotlin/hep/dataforge/vision/StyleSheet.kt | 4 +-- .../kotlin/hep/dataforge/vision/Vision.kt | 23 +++++++----- .../kotlin/hep/dataforge/vision/VisionBase.kt | 30 ++++++---------- .../hep/dataforge/vision/VisionChange.kt | 2 +- .../vision/VisionPropertyContainer.kt | 4 +-- .../kotlin/hep/dataforge/vision/misc.kt | 14 +++----- .../hep/dataforge/vision/html/HtmlTagTest.kt | 3 +- .../dataforge/vision/client/VisionClient.kt | 4 +-- .../hep/dataforge/vision/editor/FXMeta.kt | 20 +++++------ .../hep/dataforge/vision/solid/FXMaterials.kt | 17 ++++----- .../vision/solid/VisualObjectFXBinding.kt | 36 +++++++++---------- .../dataforge/vision/gdml/GdmlOptimizer.kt | 5 ++- .../hep/dataforge/vision/solid/Composite.kt | 15 ++++---- .../hep/dataforge/vision/solid/Solid.kt | 2 +- .../hep/dataforge/vision/solid/SolidGroup.kt | 6 ++-- .../dataforge/vision/solid/SolidMaterial.kt | 19 +++++----- .../dataforge/vision/solid/SolidReference.kt | 16 +++++---- .../vision/solid/specifications/Axes.kt | 2 +- .../solid/transform/RemoveSingleChild.kt | 2 +- .../hep/dataforge/vision/solid/ConvexTest.kt | 2 +- .../vision/solid/SerializationTest.kt | 5 ++- .../vision/solid/three/ThreeCanvas.kt | 4 +-- .../vision/solid/three/ThreeMaterials.kt | 8 ++--- .../hep/dataforge/vision/solid/three/three.kt | 2 +- 33 files changed, 156 insertions(+), 161 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 38355ee0..4589fe5e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,8 +2,8 @@ plugins { id("ru.mipt.npm.project") } -val dataforgeVersion by extra("0.2.1-dev-5") -val ktorVersion by extra("1.4.3") +val dataforgeVersion by extra("0.3.0-dev") +val ktorVersion by extra("1.5.0") val htmlVersion by extra("0.7.2") val kotlinWrappersVersion by extra("pre.129-kotlin-1.4.20") val fxVersion by extra("14") diff --git a/demo/gdml/src/jvmMain/kotlin/hep/dataforge/vision/gdml/demo/readFile.kt b/demo/gdml/src/jvmMain/kotlin/hep/dataforge/vision/gdml/demo/readFile.kt index da958c46..bcf18e51 100644 --- a/demo/gdml/src/jvmMain/kotlin/hep/dataforge/vision/gdml/demo/readFile.kt +++ b/demo/gdml/src/jvmMain/kotlin/hep/dataforge/vision/gdml/demo/readFile.kt @@ -1,7 +1,6 @@ package hep.dataforge.vision.gdml.demo import hep.dataforge.meta.DFExperimental -import hep.dataforge.meta.setItem import hep.dataforge.values.asValue import hep.dataforge.vision.gdml.readFile import hep.dataforge.vision.gdml.toVision diff --git a/demo/spatial-showcase/src/commonMain/kotlin/hep/dataforge/vision/solid/demo/demo.kt b/demo/spatial-showcase/src/commonMain/kotlin/hep/dataforge/vision/solid/demo/demo.kt index 76c871e6..584a2b33 100644 --- a/demo/spatial-showcase/src/commonMain/kotlin/hep/dataforge/vision/solid/demo/demo.kt +++ b/demo/spatial-showcase/src/commonMain/kotlin/hep/dataforge/vision/solid/demo/demo.kt @@ -39,15 +39,18 @@ fun Page.showcase() { demo("shapes", "Basic shapes") { box(100.0, 100.0, 100.0) { z = -110.0 + color("teal") } sphere(50.0) { x = 110 detail = 16 + color("red") } tube(50, height = 10, innerRadius = 25, angle = PI) { y = 110 detail = 16 rotationX = PI / 4 + color("blue") } } @@ -55,6 +58,7 @@ fun Page.showcase() { val group = group { box(100, 100, 100) { z = 110.0 + opacity = 0.5 } box(100, 100, 100) { @@ -155,7 +159,7 @@ fun Page.showcaseCSG() { detail = 32 } color(Colors.lightgreen) - opacity = 0.3 + opacity = 0.5 } composite(CompositeType.SUBTRACT) { y = -300 @@ -173,9 +177,11 @@ fun Page.showcaseCSG() { demo("CSG.custom", "CSG with manually created object") { intersect { tube(60, 10) { - detail = 64 + detail = 32 } box(100, 100, 100) + color("red") + opacity = 0.5 } } } \ No newline at end of file diff --git a/demo/spatial-showcase/src/jsMain/kotlin/hep/dataforge/vision/solid/demo/VariableBox.kt b/demo/spatial-showcase/src/jsMain/kotlin/hep/dataforge/vision/solid/demo/VariableBox.kt index 4fbb0903..206f4f0c 100644 --- a/demo/spatial-showcase/src/jsMain/kotlin/hep/dataforge/vision/solid/demo/VariableBox.kt +++ b/demo/spatial-showcase/src/jsMain/kotlin/hep/dataforge/vision/solid/demo/VariableBox.kt @@ -9,7 +9,6 @@ import hep.dataforge.vision.set import hep.dataforge.vision.setProperty import hep.dataforge.vision.solid.* import hep.dataforge.vision.solid.Solid.Companion.GEOMETRY_KEY -import hep.dataforge.vision.solid.SolidMaterial.Companion.MATERIAL_COLOR_KEY import hep.dataforge.vision.solid.three.* import info.laht.threekt.core.BufferGeometry import info.laht.threekt.core.Object3D @@ -30,8 +29,6 @@ internal class VariableBox(xSize: Number, ySize: Number, zSize: Number) : ThreeV scaleX = xSize scaleY = ySize scaleZ = zSize -// getProperty(MeshThreeFactory.EDGES_ENABLED_KEY, inherit = false, includeStyles = false) -// getProperty(MeshThreeFactory.WIREFRAME_ENABLED_KEY, inherit = false, includeStyles = false) } override fun render(three: ThreePlugin): Object3D { @@ -69,11 +66,8 @@ internal class VariableBox(xSize: Number, ySize: Number, zSize: Number) : ThreeV mesh.scale.set(newXSize, newYSize, newZSize) mesh.updateMatrix() } - //name.startsWith(MeshThreeFactory.WIREFRAME_KEY) -> mesh.applyWireFrame(this@VariableBox) name.startsWith(MeshThreeFactory.EDGES_KEY) -> mesh.applyEdges(this@VariableBox) - name.startsWith(MATERIAL_COLOR_KEY) -> { - mesh.updateMaterial(this) - } + //name.startsWith(MATERIAL_COLOR_KEY) -> mesh.updateMaterialProperty(this, name) else -> mesh.updateProperty(this@VariableBox, name) } } diff --git a/ui/react/src/main/kotlin/hep/dataforge/vision/react/MetaViewer.kt b/ui/react/src/main/kotlin/hep/dataforge/vision/react/MetaViewer.kt index 9c17206d..b3b1b25f 100644 --- a/ui/react/src/main/kotlin/hep/dataforge/vision/react/MetaViewer.kt +++ b/ui/react/src/main/kotlin/hep/dataforge/vision/react/MetaViewer.kt @@ -1,7 +1,8 @@ package hep.dataforge.vision.react import hep.dataforge.meta.Meta -import hep.dataforge.meta.MetaItem +import hep.dataforge.meta.NodeItem +import hep.dataforge.meta.ValueItem import hep.dataforge.meta.descriptors.ItemDescriptor import hep.dataforge.meta.descriptors.NodeDescriptor import hep.dataforge.meta.descriptors.defaultItem @@ -51,7 +52,7 @@ private fun RBuilder.metaViewerItem(props: MetaViewerProps) { } when (actualItem) { - is MetaItem.NodeItem -> { + is NodeItem -> { styledDiv { css { +TreeStyles.treeLeaf @@ -108,7 +109,7 @@ private fun RBuilder.metaViewerItem(props: MetaViewerProps) { } } } - is MetaItem.ValueItem -> { + is ValueItem -> { styledDiv { css { +TreeStyles.treeLeaf diff --git a/ui/react/src/main/kotlin/hep/dataforge/vision/react/PropertyEditor.kt b/ui/react/src/main/kotlin/hep/dataforge/vision/react/PropertyEditor.kt index ebd62473..c4eb03a0 100644 --- a/ui/react/src/main/kotlin/hep/dataforge/vision/react/PropertyEditor.kt +++ b/ui/react/src/main/kotlin/hep/dataforge/vision/react/PropertyEditor.kt @@ -9,6 +9,7 @@ import hep.dataforge.names.plus import hep.dataforge.values.Value import hep.dataforge.vision.hidden import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow @@ -65,12 +66,12 @@ private val PropertyEditorItem: FunctionalComponent = private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) { var expanded: Boolean by useState { true } val itemName by useState { props.name ?: Name.EMPTY } - var item: MetaItem<*>? by useState { props.provider.getItem(itemName) } + var item: MetaItem? by useState { props.provider.getItem(itemName) } val descriptorItem: ItemDescriptor? = props.descriptor?.get(itemName) if(descriptorItem?.hidden == true) return //fail fast for hidden property - var actualItem: MetaItem? by useState { + var actualItem: MetaItem? by useState { item ?: props.defaultProvider?.getItem(itemName) ?: descriptorItem?.defaultItem() } @@ -110,7 +111,7 @@ private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) { update() } - if (actualItem is MetaItem.NodeItem) { + if (actualItem is NodeItem) { styledDiv { css { +TreeStyles.treeLeaf @@ -248,6 +249,7 @@ public fun RBuilder.propertyEditor( } } +@OptIn(ExperimentalCoroutinesApi::class) private fun Config.flowUpdates(): Flow = callbackFlow { onChange(this) { name, _, _ -> launch { diff --git a/ui/react/src/main/kotlin/hep/dataforge/vision/react/TreeStyles.kt b/ui/react/src/main/kotlin/hep/dataforge/vision/react/TreeStyles.kt index 5afcedac..16602699 100644 --- a/ui/react/src/main/kotlin/hep/dataforge/vision/react/TreeStyles.kt +++ b/ui/react/src/main/kotlin/hep/dataforge/vision/react/TreeStyles.kt @@ -17,7 +17,7 @@ public object TreeStyles : StyleSheet("treeStyles", true) { /** * Style the caret/arrow */ - public val treeCaret by css { + public val treeCaret: RuleSet by css { cursor = Cursor.pointer userSelect = UserSelect.none /* Create the caret/arrow with a unicode, and style it */ @@ -32,7 +32,7 @@ public object TreeStyles : StyleSheet("treeStyles", true) { /** * Rotate the caret/arrow icon when clicked on (using JavaScript) */ - val treeCaredDown by css { + public val treeCaredDown:RuleSet by css { before { content = "\u25B6".quoted color = Color.black @@ -42,7 +42,7 @@ public object TreeStyles : StyleSheet("treeStyles", true) { } } - val treeItem by css { + public val treeItem:RuleSet by css { alignItems = Align.center paddingLeft = 10.px borderLeftStyle = BorderStyle.dashed @@ -53,27 +53,27 @@ public object TreeStyles : StyleSheet("treeStyles", true) { borderBottomColor = Color.lightGray } - val treeLeaf by css { + public val treeLeaf:RuleSet by css { display = Display.flex flexDirection = FlexDirection.row flexWrap = FlexWrap.nowrap //alignItems = Align.center } - val treeLabel by css { + public val treeLabel:RuleSet by css { overflow = Overflow.hidden flex(flexGrow = 1.0, flexShrink = 1.0) } - val treeLabelInactive by css { + public val treeLabelInactive: RuleSet by css { color = Color.lightGray } - val treeLabelSelected by css { + public val treeLabelSelected:RuleSet by css { backgroundColor = Color.lightBlue } - val linkButton by css { + public val linkButton:RuleSet by css { backgroundColor = Color.white border = "none" padding(left = 4.pt, right = 4.pt, top = 0.pt, bottom = 0.pt) @@ -86,7 +86,7 @@ public object TreeStyles : StyleSheet("treeStyles", true) { } } - val removeButton by css { + public val removeButton:RuleSet by css { backgroundColor = Color.white borderStyle = BorderStyle.solid borderRadius = 2.px @@ -104,7 +104,7 @@ public object TreeStyles : StyleSheet("treeStyles", true) { } } - val resizeableInput by css { + public val resizeableInput: RuleSet by css { overflow = Overflow.hidden maxWidth = 120.pt flex(flexGrow = 2.0, flexShrink = 2.0, flexBasis = 60.pt) diff --git a/ui/react/src/main/kotlin/hep/dataforge/vision/react/valueChooser.kt b/ui/react/src/main/kotlin/hep/dataforge/vision/react/valueChooser.kt index e0888a46..884791b5 100644 --- a/ui/react/src/main/kotlin/hep/dataforge/vision/react/valueChooser.kt +++ b/ui/react/src/main/kotlin/hep/dataforge/vision/react/valueChooser.kt @@ -20,7 +20,7 @@ import styled.styledInput import styled.styledSelect public external interface ValueChooserProps : RProps { - var item: MetaItem<*>? + var item: MetaItem? var descriptor: ValueDescriptor? var valueChanged: ((Value?) -> Unit)? } @@ -140,7 +140,7 @@ class ValueChooserComponent(props: ValueChooserProps) : RComponent?, + item: MetaItem?, descriptor: ValueDescriptor? = null, callback: (Value?) -> Unit ) { diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Colors.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Colors.kt index 7dc41632..605f398c 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Colors.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Colors.kt @@ -1,8 +1,6 @@ package hep.dataforge.vision -import hep.dataforge.meta.MetaItem -import hep.dataforge.meta.get -import hep.dataforge.meta.number +import hep.dataforge.meta.* import hep.dataforge.values.ValueType import hep.dataforge.values.int import hep.dataforge.values.string @@ -192,9 +190,9 @@ public object Colors { /** * Convert color represented as Meta to string of format #rrggbb */ - fun fromMeta(item: MetaItem<*>): String { + fun fromMeta(item: MetaItem): String { return when (item) { - is MetaItem.NodeItem<*> -> { + is NodeItem -> { val node = item.node rgbToString( node[RED_KEY].number?.toByte()?.toUByte() ?: 0u, @@ -202,7 +200,7 @@ public object Colors { node[BLUE_KEY].number?.toByte()?.toUByte() ?: 0u ) } - is MetaItem.ValueItem -> { + is ValueItem -> { if (item.value.type == ValueType.NUMBER) { rgbToString(item.value.int) } else { diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/StyleSheet.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/StyleSheet.kt index a22ea6bb..80d5cdd2 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/StyleSheet.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/StyleSheet.kt @@ -40,7 +40,7 @@ public inline class StyleSheet(private val owner: VisionGroup) { * Create and set a style */ public operator fun set(key: String, builder: MetaBuilder.() -> Unit) { - val newStyle = get(key)?.edit(builder) ?: Meta(builder) + val newStyle = get(key)?.builder()?.apply(builder) ?: Meta(builder) set(key, newStyle.seal()) } @@ -99,7 +99,7 @@ public tailrec fun Vision.getStyle(name: String): Meta? = /** * Resolve an item in all style layers */ -public fun Vision.getStyleItems(name: Name): Sequence> { +public fun Vision.getStyleItems(name: Name): Sequence { return styles.asSequence().map { getStyle(it) }.map { diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt index 39d5ae5a..836d6f26 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt @@ -1,5 +1,6 @@ package hep.dataforge.vision +import hep.dataforge.meta.Meta import hep.dataforge.meta.MetaItem import hep.dataforge.meta.MutableItemProvider import hep.dataforge.meta.descriptors.Described @@ -28,6 +29,12 @@ public interface Vision : Described { @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 */ @@ -37,7 +44,7 @@ public interface Vision : Described { * 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<*>? + public fun getOwnProperty(name: Name): MetaItem? /** * Get property. @@ -49,13 +56,13 @@ public interface Vision : Described { inherit: Boolean = false, includeStyles: Boolean = true, includeDefaults: Boolean = true, - ): MetaItem<*>? + ): MetaItem? /** * Set the property value */ - public fun setProperty(name: Name, item: MetaItem<*>?, notify: Boolean = true) + 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 @@ -109,8 +116,8 @@ public fun Vision.asyncNotifyPropertyChange(propertyName: Name) { */ public val Vision.ownProperties: MutableItemProvider get() = object : MutableItemProvider { - override fun getItem(name: Name): MetaItem<*>? = getOwnProperty(name) - override fun setItem(name: Name, item: MetaItem<*>?): Unit = setProperty(name, item) + override fun getItem(name: Name): MetaItem? = getOwnProperty(name) + override fun setItem(name: Name, item: MetaItem?): Unit = setProperty(name, item) } @@ -123,14 +130,14 @@ public fun Vision.allProperties( includeStyles: Boolean? = null, includeDefaults: Boolean = true, ): MutableItemProvider = object : MutableItemProvider { - override fun getItem(name: Name): MetaItem<*>? = getProperty( + override fun getItem(name: Name): MetaItem? = getProperty( name, inherit = inherit ?: (descriptor?.get(name)?.inherited != false), includeStyles = includeStyles ?: (descriptor?.get(name)?.usesStyles == true), includeDefaults = includeDefaults ) - override fun setItem(name: Name, item: MetaItem<*>?): Unit = setProperty(name, item) + override fun setItem(name: Name, item: MetaItem?): Unit = setProperty(name, item) } /** @@ -141,7 +148,7 @@ public fun Vision.getProperty( inherit: Boolean = false, includeStyles: Boolean = true, includeDefaults: Boolean = true, -): MetaItem<*>? = getProperty(key.toName(), inherit, includeStyles, includeDefaults) +): MetaItem? = getProperty(key.toName(), inherit, includeStyles, includeDefaults) /** * A convenience method to pair [getProperty] diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt index a70cdf21..753498f2 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt @@ -1,9 +1,6 @@ package hep.dataforge.vision -import hep.dataforge.meta.Config -import hep.dataforge.meta.MetaItem -import hep.dataforge.meta.MutableMeta -import hep.dataforge.meta.asMetaItem +import hep.dataforge.meta.* import hep.dataforge.meta.descriptors.NodeDescriptor import hep.dataforge.meta.descriptors.defaultItem import hep.dataforge.meta.descriptors.get @@ -40,14 +37,14 @@ public open class VisionBase : Vision { /** * Object own properties excluding styles and inheritance */ - public var properties: Config? = null - private set + internal var properties: Config? = null + + override val meta: Meta get() = properties ?: Meta.EMPTY @Synchronized private fun getOrCreateConfig(): Config { if (properties == null) { val newProperties = Config() - properties = newProperties newProperties.onChange(this) { name, oldItem, newItem -> if (oldItem != newItem) { scope.launch { @@ -55,6 +52,7 @@ public open class VisionBase : Vision { } } } + properties = newProperties } return properties!! } @@ -62,7 +60,7 @@ public open class VisionBase : Vision { /** * A fast accessor method to get own property (no inheritance or styles */ - override fun getOwnProperty(name: Name): MetaItem<*>? { + override fun getOwnProperty(name: Name): MetaItem? { return properties?.getItem(name) } @@ -71,7 +69,7 @@ public open class VisionBase : Vision { inherit: Boolean, includeStyles: Boolean, includeDefaults: Boolean, - ): MetaItem<*>? = sequence { + ): MetaItem? = sequence { yield(getOwnProperty(name)) if (includeStyles) { yieldAll(getStyleItems(name)) @@ -83,7 +81,7 @@ public open class VisionBase : Vision { }.merge() @Synchronized - override fun setProperty(name: Name, item: MetaItem<*>?, notify: Boolean) { + override fun setProperty(name: Name, item: MetaItem?, notify: Boolean) { getOrCreateConfig().setItem(name, item) if (notify) { scope.launch { @@ -121,23 +119,17 @@ public open class VisionBase : Vision { propertyInvalidationFlow.emit(propertyName) } - public fun configure(block: suspend MutableMeta<*>.() -> Unit) { - scope.launch { - getOrCreateConfig().block() - } - } - override fun update(change: VisionChange) { - fun updateProperties(at: Name, item: MetaItem<*>) { + fun updateProperties(at: Name, item: MetaItem) { when (item) { - is MetaItem.ValueItem -> { + is ValueItem -> { if (item.value == Null) { setProperty(at, null) } else setProperty(at, item) } - is MetaItem.NodeItem -> item.node.items.forEach { (token, childItem) -> + is NodeItem -> item.node.items.forEach { (token, childItem) -> updateProperties(at + token, childItem) } } diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionChange.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionChange.kt index 2bfde782..8d799ef8 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionChange.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionChange.kt @@ -29,7 +29,7 @@ public class VisionChangeBuilder : VisionContainerBuilder { private fun getOrPutChild(visionName: Name): VisionChangeBuilder = children.getOrPut(visionName) { VisionChangeBuilder() } - public fun propertyChanged(visionName: Name, propertyName: Name, item: MetaItem<*>?) { + public fun propertyChanged(visionName: Name, propertyName: Name, item: MetaItem?) { if (visionName == Name.EMPTY) { //Write property removal as [Null] propertyChange[propertyName] = (item ?: Null.asMetaItem()) diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionPropertyContainer.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionPropertyContainer.kt index f206f063..a8b3ecad 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionPropertyContainer.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionPropertyContainer.kt @@ -12,7 +12,7 @@ public interface VisionPropertyContainer { inherit: Boolean = false, includeStyles: Boolean = true, includeDefaults: Boolean = true, - ): MetaItem<*>? + ): MetaItem? - public fun setProperty(name: Name, item: MetaItem<*>?, notify: Boolean = true) + public fun setProperty(name: Name, item: MetaItem?, notify: Boolean = true) } \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/misc.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/misc.kt index 047cd2fa..fb070582 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/misc.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/misc.kt @@ -6,20 +6,16 @@ import hep.dataforge.values.asValue @DslMarker public annotation class VisionBuilder -public fun Sequence?>.merge(): MetaItem<*>? = when (val first = firstOrNull { it != null }) { +public fun Sequence.merge(): MetaItem? = when (val first = firstOrNull { it != null }) { null -> null - is MetaItem.ValueItem -> first //fast search for first entry if it is value - is MetaItem.NodeItem -> { + is ValueItem -> first //fast search for first entry if it is value + is NodeItem -> { //merge nodes if first encountered node is meta val laminate: Laminate = Laminate(mapNotNull { it.node }.toList()) - MetaItem.NodeItem(laminate) + NodeItem(laminate) } } -@DFExperimental -public val Vision.properties: Config? - get() = (this as? VisionBase)?.properties - /** * Control visibility of the element */ @@ -29,4 +25,4 @@ public var Vision.visible: Boolean? public fun Vision.configure(meta: Meta?): Unit = update(VisionChange(properties = meta)) -public fun Vision.configure(block: MutableMeta<*>.() -> Unit): Unit = configure(Meta(block)) \ No newline at end of file +public fun Vision.configure(block: MetaBuilder.() -> Unit): Unit = configure(Meta(block)) \ No newline at end of file diff --git a/visionforge-core/src/commonTest/kotlin/hep/dataforge/vision/html/HtmlTagTest.kt b/visionforge-core/src/commonTest/kotlin/hep/dataforge/vision/html/HtmlTagTest.kt index c6efe090..2d2ad125 100644 --- a/visionforge-core/src/commonTest/kotlin/hep/dataforge/vision/html/HtmlTagTest.kt +++ b/visionforge-core/src/commonTest/kotlin/hep/dataforge/vision/html/HtmlTagTest.kt @@ -3,6 +3,7 @@ package hep.dataforge.vision.html import hep.dataforge.meta.DFExperimental import hep.dataforge.meta.set import hep.dataforge.vision.VisionBase +import hep.dataforge.vision.configure import kotlinx.html.* import kotlinx.html.stream.createHTML import kotlin.test.Test @@ -34,7 +35,7 @@ class HtmlTagTest { div { h2 { +"Properties" } ul { - (vision as? VisionBase)?.properties?.items?.forEach { + (vision as? VisionBase)?.meta?.items?.forEach { li { a { +it.key.toString() } p { +it.value.toString() } diff --git a/visionforge-core/src/jsMain/kotlin/hep/dataforge/vision/client/VisionClient.kt b/visionforge-core/src/jsMain/kotlin/hep/dataforge/vision/client/VisionClient.kt index 11eaa0e1..edd5ae4d 100644 --- a/visionforge-core/src/jsMain/kotlin/hep/dataforge/vision/client/VisionClient.kt +++ b/visionforge-core/src/jsMain/kotlin/hep/dataforge/vision/client/VisionClient.kt @@ -134,11 +134,11 @@ public class VisionClient : AbstractPlugin() { stringData ) - if(change.vision!= null){ + if (change.vision != null) { renderVision(element, change.vision, outputMeta) } - logger.info { "Got update ${change.toString()} for output with name $name" } + logger.debug { "Got update $change for output with name $name" } visionMap[element]?.update(change) ?: console.info("Target vision for element $element with name $name not found") } else { diff --git a/visionforge-fx/src/main/kotlin/hep/dataforge/vision/editor/FXMeta.kt b/visionforge-fx/src/main/kotlin/hep/dataforge/vision/editor/FXMeta.kt index 84a286ed..c7460ff3 100644 --- a/visionforge-fx/src/main/kotlin/hep/dataforge/vision/editor/FXMeta.kt +++ b/visionforge-fx/src/main/kotlin/hep/dataforge/vision/editor/FXMeta.kt @@ -18,7 +18,7 @@ import tornadofx.* /** * A display for meta and descriptor */ -sealed class FXMeta> : Comparable> { +sealed class FXMeta> : Comparable> { abstract val name: NameToken abstract val parent: FXMetaNode? abstract val descriptionProperty: ObservableStringValue @@ -35,7 +35,7 @@ sealed class FXMeta> : Comparable> { } companion object { - fun > root( + fun > root( node: M, descriptor: NodeDescriptor? = null, rootName: String = "root" @@ -47,7 +47,7 @@ sealed class FXMeta> : Comparable> { } } -class FXMetaNode>( +class FXMetaNode>( override val name: NameToken, override val parent: FXMetaNode?, nodeValue: M? = null, @@ -89,7 +89,7 @@ class FXMetaNode>( init { bind(nodeProperty, descriptorProperty) - val listener: (Name, MetaItem<*>?, MetaItem<*>?) -> Unit = { name, _, _ -> + val listener: (Name, MetaItem?, MetaItem?) -> Unit = { name, _, _ -> if (name.length == 1) invalidate() } @@ -115,7 +115,7 @@ class FXMetaNode>( val actualItem = node?.items?.get(token) val actualDescriptor = descriptor?.items?.get(token.body) - if (actualItem is MetaItem.NodeItem || actualDescriptor is NodeDescriptor) { + if (actualItem is NodeItem || actualDescriptor is NodeDescriptor) { FXMetaNode(token, this@FXMetaNode) } else { FXMetaValue(token, this@FXMetaNode) @@ -134,7 +134,7 @@ class FXMetaNode>( } } -public class FXMetaValue>( +public class FXMetaValue>( override val name: NameToken, override val parent: FXMetaNode ) : FXMeta() { @@ -151,10 +151,10 @@ public class FXMetaValue>( //private val innerValueProperty = SimpleObjectProperty(value) public val valueProperty = descriptorProperty.objectBinding { descriptor -> - parent.node[name].value ?: descriptor?.default + parent.node?.get(name).value ?: descriptor?.default } - override val hasValue: ObservableBooleanValue = parent.nodeProperty.booleanBinding { it[name] != null } + override val hasValue: ObservableBooleanValue = parent.nodeProperty.booleanBinding { it?.get(name) != null } public val value by valueProperty @@ -169,12 +169,12 @@ public fun > FXMetaNode.remove(name: NameToken) { private fun > M.createEmptyNode(token: NameToken, append: Boolean): M { return if (append && token.hasIndex()) { val name = token.asName() - val index = (getIndexed(name).keys.mapNotNull { it.toIntOrNull() }.max() ?: -1) + 1 + val index = (getIndexed(name).keys.mapNotNull { it?.toIntOrNull() }.maxOrNull() ?: -1) + 1 val newName = name.withIndex(index.toString()) set(newName, Meta.EMPTY) get(newName).node!! } else { - this.setNode(token.asName(), Meta.EMPTY) + this.set(token.asName(), Meta.EMPTY) //FIXME possible concurrency bug get(token).node!! } diff --git a/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/FXMaterials.kt b/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/FXMaterials.kt index 9ae8fcf3..9e417438 100644 --- a/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/FXMaterials.kt +++ b/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/FXMaterials.kt @@ -1,9 +1,6 @@ package hep.dataforge.vision.solid -import hep.dataforge.meta.MetaItem -import hep.dataforge.meta.double -import hep.dataforge.meta.get -import hep.dataforge.meta.int +import hep.dataforge.meta.* import hep.dataforge.values.ValueType import hep.dataforge.values.int import hep.dataforge.values.string @@ -35,9 +32,9 @@ public object FXMaterials { * Infer color based on meta item * @param opacity default opacity */ -public fun MetaItem<*>.color(opacity: Double = 1.0): Color { +public fun MetaItem.color(opacity: Double = 1.0): Color { return when (this) { - is MetaItem.ValueItem -> if (this.value.type == ValueType.NUMBER) { + is ValueItem -> if (this.value.type == ValueType.NUMBER) { val int = value.int val red = int and 0x00ff0000 shr 16 val green = int and 0x0000ff00 shr 8 @@ -46,7 +43,7 @@ public fun MetaItem<*>.color(opacity: Double = 1.0): Color { } else { Color.web(this.value.string) } - is MetaItem.NodeItem -> { + is NodeItem -> { Color.rgb( node[Colors.RED_KEY]?.int ?: 0, node[Colors.GREEN_KEY]?.int ?: 0, @@ -60,11 +57,11 @@ public fun MetaItem<*>.color(opacity: Double = 1.0): Color { /** * Infer FX material based on meta item */ -public fun MetaItem<*>?.material(): Material { +public fun MetaItem?.material(): Material { return when (this) { null -> FXMaterials.GREY - is MetaItem.ValueItem -> PhongMaterial(color()) - is MetaItem.NodeItem -> PhongMaterial().apply { + is ValueItem -> PhongMaterial(color()) + is NodeItem -> PhongMaterial().apply { val opacity = node[SolidMaterial.OPACITY_KEY].double ?: 1.0 diffuseColor = node[SolidMaterial.COLOR_KEY]?.color(opacity) ?: Color.DARKGREY specularColor = node[SolidMaterial.SPECULAR_COLOR_KEY]?.color(opacity) ?: Color.WHITE diff --git a/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/VisualObjectFXBinding.kt b/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/VisualObjectFXBinding.kt index 75d9307c..f6f72e54 100644 --- a/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/VisualObjectFXBinding.kt +++ b/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/VisualObjectFXBinding.kt @@ -13,7 +13,7 @@ import tornadofx.* * A caching binding collection for [Vision] properties */ class VisualObjectFXBinding(val fx: FX3DPlugin, val obj: Vision) { - private val bindings = HashMap?>>() + private val bindings = HashMap>() init { obj.onPropertyChange(fx.context) { name -> @@ -31,10 +31,10 @@ class VisualObjectFXBinding(val fx: FX3DPlugin, val obj: Vision) { } } - operator fun get(key: Name): ObjectBinding?> { + operator fun get(key: Name): ObjectBinding { return bindings.getOrPut(key) { - object : ObjectBinding?>() { - override fun computeValue(): MetaItem<*>? = obj.getProperty(key) + object : ObjectBinding() { + override fun computeValue(): MetaItem? = obj.getProperty(key) } } } @@ -42,19 +42,19 @@ class VisualObjectFXBinding(val fx: FX3DPlugin, val obj: Vision) { operator fun get(key: String) = get(key.toName()) } -fun ObjectBinding?>.value() = objectBinding { it.value } -fun ObjectBinding?>.string() = stringBinding { it.string } -fun ObjectBinding?>.number() = objectBinding { it.number } -fun ObjectBinding?>.double() = objectBinding { it.double } -fun ObjectBinding?>.float() = objectBinding { it.float } -fun ObjectBinding?>.int() = objectBinding { it.int } -fun ObjectBinding?>.long() = objectBinding { it.long } -fun ObjectBinding?>.node() = objectBinding { it.node } +fun ObjectBinding.value() = objectBinding { it.value } +fun ObjectBinding.string() = stringBinding { it.string } +fun ObjectBinding.number() = objectBinding { it.number } +fun ObjectBinding.double() = objectBinding { it.double } +fun ObjectBinding.float() = objectBinding { it.float } +fun ObjectBinding.int() = objectBinding { it.int } +fun ObjectBinding.long() = objectBinding { it.long } +fun ObjectBinding.node() = objectBinding { it.node } -fun ObjectBinding?>.string(default: String) = stringBinding { it.string ?: default } -fun ObjectBinding?>.double(default: Double) = doubleBinding { it.double ?: default } -fun ObjectBinding?>.float(default: Float) = floatBinding { it.float ?: default } -fun ObjectBinding?>.int(default: Int) = integerBinding { it.int ?: default } -fun ObjectBinding?>.long(default: Long) = longBinding { it.long ?: default } +fun ObjectBinding.string(default: String) = stringBinding { it.string ?: default } +fun ObjectBinding.double(default: Double) = doubleBinding { it.double ?: default } +fun ObjectBinding.float(default: Float) = floatBinding { it.float ?: default } +fun ObjectBinding.int(default: Int) = integerBinding { it.int ?: default } +fun ObjectBinding.long(default: Long) = longBinding { it.long ?: default } -fun ObjectBinding?>.transform(transform: (MetaItem<*>) -> T) = objectBinding { it?.let(transform) } +fun ObjectBinding.transform(transform: (MetaItem) -> T) = objectBinding { it?.let(transform) } diff --git a/visionforge-gdml/src/commonMain/kotlin/hep/dataforge/vision/gdml/GdmlOptimizer.kt b/visionforge-gdml/src/commonMain/kotlin/hep/dataforge/vision/gdml/GdmlOptimizer.kt index 125614c6..a01e3db0 100644 --- a/visionforge-gdml/src/commonMain/kotlin/hep/dataforge/vision/gdml/GdmlOptimizer.kt +++ b/visionforge-gdml/src/commonMain/kotlin/hep/dataforge/vision/gdml/GdmlOptimizer.kt @@ -1,9 +1,8 @@ package hep.dataforge.vision.gdml import hep.dataforge.meta.DFExperimental -import hep.dataforge.meta.sequence +import hep.dataforge.meta.itemSequence import hep.dataforge.vision.Vision -import hep.dataforge.vision.properties import hep.dataforge.vision.solid.* public expect class Counter() { @@ -27,7 +26,7 @@ internal fun Vision.updateFrom(other: Vision): Vision { scaleY = scaleY.toDouble() * other.scaleY.toDouble() scaleZ = scaleZ.toDouble() * other.scaleZ.toDouble() } - other.properties?.sequence()?.forEach { (name, item) -> + other.meta.itemSequence().forEach { (name, item) -> if (getProperty(name) == null) { setProperty(name, item) } diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Composite.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Composite.kt index e1e79ef8..6dc96076 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Composite.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Composite.kt @@ -1,6 +1,5 @@ package hep.dataforge.vision.solid -import hep.dataforge.meta.Meta import hep.dataforge.meta.update import hep.dataforge.names.NameToken import hep.dataforge.vision.* @@ -39,18 +38,20 @@ public inline fun VisionContainerBuilder.composite( val group = SolidGroup().apply(builder) val children = group.children.values.filterIsInstance() if (children.size != 2) error("Composite requires exactly two children") - return Composite(type, children[0], children[1]).also { - it.configure { update(group.properties ?: Meta.EMPTY) } + return Composite(type, children[0], children[1]).also { composite -> + composite.configure { + update(group.meta) + } if (group.position != null) { - it.position = group.position + composite.position = group.position } if (group.rotation != null) { - it.rotation = group.rotation + composite.rotation = group.rotation } if (group.scale != null) { - it.scale = group.scale + composite.scale = group.scale } - set(name, it) + set(name, composite) } } diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Solid.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Solid.kt index 987fec3c..61710713 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Solid.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Solid.kt @@ -85,7 +85,7 @@ public interface Solid : Vision { if (first.position != second.position) return false if (first.rotation != second.rotation) return false if (first.scale != second.scale) return false - if (first.properties != second.properties) return false + if (first.meta != second.meta) return false return true } diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt index b64074da..3b49f2fa 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt @@ -106,16 +106,16 @@ internal class Prototypes( } } - override fun getOwnProperty(name: Name): MetaItem<*>? = null + override fun getOwnProperty(name: Name): MetaItem? = null override fun getProperty( name: Name, inherit: Boolean, includeStyles: Boolean, includeDefaults: Boolean, - ): MetaItem<*>? = null + ): MetaItem? = null - override fun setProperty(name: Name, item: MetaItem<*>?, notify: Boolean) { + override fun setProperty(name: Name, item: MetaItem?, notify: Boolean) { error("Can't ser property of prototypes container") } diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidMaterial.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidMaterial.kt index 2394030b..3e81748e 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidMaterial.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidMaterial.kt @@ -19,12 +19,12 @@ public class SolidMaterial : Scheme() { /** * Primary web-color for the material */ - public var color: ColorAccessor = ColorAccessor(config, COLOR_KEY) + public var color: ColorAccessor = ColorAccessor(items, COLOR_KEY) /** * Specular color for phong material */ - public var specularColor: ColorAccessor = ColorAccessor(config, SPECULAR_COLOR_KEY) + public var specularColor: ColorAccessor = ColorAccessor(items, SPECULAR_COLOR_KEY) /** * Opacity @@ -100,16 +100,17 @@ public val Solid.color: ColorAccessor public var Solid.material: SolidMaterial? get() = getProperty(MATERIAL_KEY, inherit = true).node?.let { SolidMaterial.read(it) } - set(value) = setProperty(MATERIAL_KEY, value?.config) + set(value) = setProperty(MATERIAL_KEY, value?.rootNode) @VisionBuilder public fun Solid.material(builder: SolidMaterial.() -> Unit) { - val node = allProperties(inherit = true).getItem(MATERIAL_KEY).node - if (node != null) { - SolidMaterial.update(node, builder) - } else { - setProperty(MATERIAL_KEY, SolidMaterial(builder)) - } + setProperty(MATERIAL_KEY, SolidMaterial(builder)) +// val node = getOwnProperty(MATERIAL_KEY).node +// if (node != null) { +// configure(SolidMaterial(builder).config) +// } else { +// setProperty(MATERIAL_KEY, SolidMaterial(builder)) +// } } public var Solid.opacity: Number? diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidReference.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidReference.kt index d9fbdba0..261f16d8 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidReference.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidReference.kt @@ -17,7 +17,7 @@ private fun SolidReference.getRefProperty( inherit: Boolean, includeStyles: Boolean, includeDefaults: Boolean, -): MetaItem<*>? { +): MetaItem? { return sequence { yield(getOwnProperty(name)) if (includeStyles) { @@ -59,11 +59,11 @@ public class SolidReferenceGroup( private fun childPropertyName(childName: Name, propertyName: Name): Name = childToken(childName) + propertyName - private fun getChildProperty(childName: Name, propertyName: Name): MetaItem<*>? { + private fun getChildProperty(childName: Name, propertyName: Name): MetaItem? { return getOwnProperty(childPropertyName(childName, propertyName)) } - private fun setChildProperty(childName: Name, propertyName: Name, item: MetaItem<*>?, notify: Boolean) { + private fun setChildProperty(childName: Name, propertyName: Name, item: MetaItem?, notify: Boolean) { setProperty(childPropertyName(childName, propertyName), item, notify) } @@ -79,7 +79,7 @@ public class SolidReferenceGroup( inherit: Boolean, includeStyles: Boolean, includeDefaults: Boolean, - ): MetaItem<*>? = getRefProperty(name, inherit, includeStyles, includeDefaults) + ): MetaItem? = getRefProperty(name, inherit, includeStyles, includeDefaults) override val descriptor: NodeDescriptor get() = prototype.descriptor @@ -99,9 +99,11 @@ public class SolidReferenceGroup( ReferenceChild(childName + key.asName()) } ?: emptyMap() - override fun getOwnProperty(name: Name): MetaItem<*>? = getChildProperty(childName, name) + override val meta: Meta get() =TODO()// getChildProperty(childName, Name.EMPTY).node ?: Meta.EMPTY - override fun setProperty(name: Name, item: MetaItem<*>?, notify: Boolean) { + override fun getOwnProperty(name: Name): MetaItem? = getChildProperty(childName, name) + + override fun setProperty(name: Name, item: MetaItem?, notify: Boolean) { setChildProperty(childName, name, item, notify) } @@ -110,7 +112,7 @@ public class SolidReferenceGroup( inherit: Boolean, includeStyles: Boolean, includeDefaults: Boolean, - ): MetaItem<*>? = getRefProperty(name, inherit, includeStyles, includeDefaults) + ): MetaItem? = getRefProperty(name, inherit, includeStyles, includeDefaults) override var parent: VisionGroup? get() { diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/specifications/Axes.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/specifications/Axes.kt index 5e835c83..022fd028 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/specifications/Axes.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/specifications/Axes.kt @@ -3,7 +3,7 @@ package hep.dataforge.vision.solid.specifications import hep.dataforge.meta.* public class Axes : Scheme() { - public var visible: Boolean by boolean(!config.isEmpty()) + public var visible: Boolean by boolean(rootNode?.isEmpty() != false) public var size: Double by double(AXIS_SIZE) public var width: Double by double(AXIS_WIDTH) diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/transform/RemoveSingleChild.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/transform/RemoveSingleChild.kt index a5b5f1ba..0b69d23b 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/transform/RemoveSingleChild.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/transform/RemoveSingleChild.kt @@ -9,7 +9,7 @@ import hep.dataforge.vision.solid.* internal fun mergeChild(parent: VisionGroup, child: Vision): Vision { return child.apply { - configure(parent.properties) + configure(parent.meta) //parent.properties?.let { config.update(it) } diff --git a/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/ConvexTest.kt b/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/ConvexTest.kt index d2dbc4b3..912e3b5a 100644 --- a/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/ConvexTest.kt +++ b/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/ConvexTest.kt @@ -28,7 +28,7 @@ class ConvexTest { val json = SolidManager.jsonForSolids.encodeToJsonElement(Convex.serializer(), convex) val meta = json.toMetaItem().node!! - val points = meta.getIndexed("points").values.map { (it as MetaItem.NodeItem<*>).node.point3D() } + val points = meta.getIndexed("points").values.map { (it as NodeItem<*>).node.point3D() } assertEquals(8, points.count()) assertEquals(8, convex.points.size) diff --git a/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/SerializationTest.kt b/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/SerializationTest.kt index c7090eee..10531796 100644 --- a/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/SerializationTest.kt +++ b/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/SerializationTest.kt @@ -4,7 +4,6 @@ import hep.dataforge.names.Name import hep.dataforge.names.toName import hep.dataforge.vision.MutableVisionGroup import hep.dataforge.vision.get -import hep.dataforge.vision.properties import kotlin.test.Test import kotlin.test.assertEquals @@ -33,7 +32,7 @@ class SerializationTest { val string = SolidManager.encodeToString(cube) println(string) val newCube = SolidManager.decodeFromString(string) - assertEquals(cube.properties, newCube.properties) + assertEquals(cube.meta, newCube.meta) } @Test @@ -54,7 +53,7 @@ class SerializationTest { val string = SolidManager.encodeToString(group) println(string) val reconstructed = SolidManager.decodeFromString(string) as SolidGroup - assertEquals(group["cube"]?.properties, reconstructed["cube"]?.properties) + assertEquals(group["cube"]?.meta, reconstructed["cube"]?.meta) } } \ No newline at end of file diff --git a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeCanvas.kt b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeCanvas.kt index f5b679e5..a17ab549 100644 --- a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeCanvas.kt +++ b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeCanvas.kt @@ -1,6 +1,6 @@ package hep.dataforge.vision.solid.three -import hep.dataforge.meta.getItem +import hep.dataforge.meta.get import hep.dataforge.meta.string import hep.dataforge.names.Name import hep.dataforge.names.plus @@ -177,7 +177,7 @@ public class ThreeCanvas( } private fun addControls(element: Node, controls: Controls) { - when (controls.getItem("type").string) { + when (controls.get("type").string) { "trackball" -> TrackballControls(camera, element) else -> OrbitControls(camera, element) } diff --git a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeMaterials.kt b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeMaterials.kt index e9471af8..2d459217 100644 --- a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeMaterials.kt +++ b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeMaterials.kt @@ -109,15 +109,15 @@ public object ThreeMaterials { /** * Infer color based on meta item */ -public fun MetaItem<*>.getColor(): Color { +public fun MetaItem.getColor(): Color { return when (this) { - is MetaItem.ValueItem -> if (this.value.type == ValueType.NUMBER) { + is ValueItem -> if (this.value.type == ValueType.NUMBER) { val int = value.int Color(int) } else { Color(this.value.string) } - is MetaItem.NodeItem -> { + is NodeItem -> { Color( node[Colors.RED_KEY]?.int ?: 0, node[Colors.GREEN_KEY]?.int ?: 0, @@ -160,7 +160,7 @@ public fun Mesh.updateMaterial(vision: Vision) { } public fun Mesh.updateMaterialProperty(vision: Vision, propertyName: Name) { - if (material.cached) { + if (material.cached || propertyName == SolidMaterial.MATERIAL_KEY) { //generate a new material since cached material should not be changed updateMaterial(vision) } else { diff --git a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/three.kt b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/three.kt index 862960a9..9a11c550 100644 --- a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/three.kt +++ b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/three.kt @@ -17,7 +17,7 @@ import kotlin.math.PI public val Solid.euler: Euler get() = Euler(rotationX, rotationY, rotationZ, rotationOrder.name) -public val MetaItem<*>.vector: Vector3 get() = Vector3(node["x"].float ?: 0f, node["y"].float ?: 0f, node["z"].float ?: 0f) +public val MetaItem.vector: Vector3 get() = Vector3(node["x"].float ?: 0f, node["y"].float ?: 0f, node["z"].float ?: 0f) public fun Geometry.toBufferGeometry(): BufferGeometry = BufferGeometry().apply { fromGeometry(this@toBufferGeometry) } From 2b02f151d283c9daeff8b9f228d3a508e17a64ce Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 25 Dec 2020 19:44:53 +0300 Subject: [PATCH 15/19] Migrate to DF 0.3 --- .../kotlin/ru/mipt/npm/muon/monitor/Model.kt | 1 - .../hep/dataforge/vision/solid/demo/demo.kt | 9 +++--- .../kotlin/hep/dataforge/vision/VisionBase.kt | 23 +++++++++++---- .../dataforge/vision/solid/ColorAccessor.kt | 2 +- .../hep/dataforge/vision/solid/SolidGroup.kt | 1 + .../dataforge/vision/solid/SolidMaterial.kt | 12 ++------ .../solid/specifications/Canvas3DOptions.kt | 6 ++-- .../dataforge/vision/solid/CompositeTest.kt | 29 +++++++++++++++++++ 8 files changed, 58 insertions(+), 25 deletions(-) create mode 100644 visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/CompositeTest.kt diff --git a/demo/muon-monitor/src/commonMain/kotlin/ru/mipt/npm/muon/monitor/Model.kt b/demo/muon-monitor/src/commonMain/kotlin/ru/mipt/npm/muon/monitor/Model.kt index 9027b26a..b4b883fc 100644 --- a/demo/muon-monitor/src/commonMain/kotlin/ru/mipt/npm/muon/monitor/Model.kt +++ b/demo/muon-monitor/src/commonMain/kotlin/ru/mipt/npm/muon/monitor/Model.kt @@ -52,7 +52,6 @@ class Model { detector(it) } } - tracks = group("tracks") } diff --git a/demo/spatial-showcase/src/commonMain/kotlin/hep/dataforge/vision/solid/demo/demo.kt b/demo/spatial-showcase/src/commonMain/kotlin/hep/dataforge/vision/solid/demo/demo.kt index 584a2b33..2dacf455 100644 --- a/demo/spatial-showcase/src/commonMain/kotlin/hep/dataforge/vision/solid/demo/demo.kt +++ b/demo/spatial-showcase/src/commonMain/kotlin/hep/dataforge/vision/solid/demo/demo.kt @@ -147,8 +147,7 @@ fun Page.showcaseCSG() { detail = 32 } material { - color(Colors.red) - wireframe = false + color(Colors.pink) } } composite(CompositeType.UNION) { @@ -158,8 +157,8 @@ fun Page.showcaseCSG() { sphere(50){ detail = 32 } - color(Colors.lightgreen) - opacity = 0.5 + color("lightgreen") + opacity = 0.7 } composite(CompositeType.SUBTRACT) { y = -300 @@ -169,7 +168,7 @@ fun Page.showcaseCSG() { sphere(50){ detail = 32 } - color(Colors.teal) + color("teal") opacity = 0.7 } } diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt index 753498f2..899c2877 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt @@ -16,6 +16,7 @@ import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch +import kotlinx.css.th import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.Transient @@ -27,18 +28,28 @@ internal data class PropertyListener( ) +/** + * A full base implementation for a [Vision] + * @param properties Object own properties excluding styles and inheritance + */ @Serializable @SerialName("vision") -public open class VisionBase : Vision { +public open class VisionBase(internal var properties: Config? = null) : Vision { + + init { + //used during deserialization only + properties?.onChange(this) { name, oldItem, newItem -> + if (oldItem != newItem) { + scope.launch { + notifyPropertyChanged(name) + } + } + } + } @Transient override var parent: VisionGroup? = null - /** - * Object own properties excluding styles and inheritance - */ - internal var properties: Config? = null - override val meta: Meta get() = properties ?: Meta.EMPTY @Synchronized diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/ColorAccessor.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/ColorAccessor.kt index 3b78d7b5..05e8cad9 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/ColorAccessor.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/ColorAccessor.kt @@ -46,6 +46,6 @@ public operator fun ColorAccessor?.invoke(r: UByte, g: UByte, b: UByte) { this?.value = Colors.rgbToString(r, g, b).asValue() } -public fun ColorAccessor?.clear(){ +public fun ColorAccessor?.clear() { this?.value = null } \ No newline at end of file diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt index 3b49f2fa..578a06e6 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt @@ -101,6 +101,7 @@ internal class Prototypes( ) : VisionGroupBase(children as? MutableMap ?: children.toMutableMap()), PrototypeHolder { init { + //used during deserialization only children.values.forEach { it.parent = parent } diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidMaterial.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidMaterial.kt index 3e81748e..314ea57e 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidMaterial.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidMaterial.kt @@ -19,12 +19,12 @@ public class SolidMaterial : Scheme() { /** * Primary web-color for the material */ - public var color: ColorAccessor = ColorAccessor(items, COLOR_KEY) + public var color: ColorAccessor = ColorAccessor(this, COLOR_KEY) /** * Specular color for phong material */ - public var specularColor: ColorAccessor = ColorAccessor(items, SPECULAR_COLOR_KEY) + public var specularColor: ColorAccessor = ColorAccessor(this, SPECULAR_COLOR_KEY) /** * Opacity @@ -104,13 +104,7 @@ public var Solid.material: SolidMaterial? @VisionBuilder public fun Solid.material(builder: SolidMaterial.() -> Unit) { - setProperty(MATERIAL_KEY, SolidMaterial(builder)) -// val node = getOwnProperty(MATERIAL_KEY).node -// if (node != null) { -// configure(SolidMaterial(builder).config) -// } else { -// setProperty(MATERIAL_KEY, SolidMaterial(builder)) -// } + ownProperties.getChild(MATERIAL_KEY).update(SolidMaterial, builder) } public var Solid.opacity: Number? diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/specifications/Canvas3DOptions.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/specifications/Canvas3DOptions.kt index b62ea275..cfe59171 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/specifications/Canvas3DOptions.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/specifications/Canvas3DOptions.kt @@ -4,9 +4,9 @@ import hep.dataforge.meta.* import hep.dataforge.names.Name public class Canvas3DOptions : Scheme() { - public var axes: Axes by spec(Axes, Axes.empty()) - public var camera: Camera by spec(Camera, Camera.empty()) - public var controls: Controls by spec(Controls, Controls.empty()) + public var axes: Axes by spec(Axes) + public var camera: Camera by spec(Camera) + public var controls: Controls by spec(Controls) public var minSize: Int by int(400) public var minWith: Number by number { minSize } diff --git a/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/CompositeTest.kt b/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/CompositeTest.kt new file mode 100644 index 00000000..45cc6d49 --- /dev/null +++ b/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/CompositeTest.kt @@ -0,0 +1,29 @@ +package hep.dataforge.vision.solid + +import hep.dataforge.vision.Colors +import kotlin.test.Test +import kotlin.test.assertEquals + +class CompositeTest { + + @Test + fun testCompositeBuilder(){ + lateinit var composite: Composite + SolidGroup { + composite = composite(CompositeType.INTERSECT) { + y = 300 + box(100, 100, 100) { + z = 50 + } + sphere(50) { + detail = 32 + } + material { + color("pink") + } + } + } + + assertEquals("pink", composite.color.string) + } +} \ No newline at end of file From 346b61724f0101b601b16d630a86ce4861754141 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 26 Dec 2020 17:11:52 +0300 Subject: [PATCH 16/19] Fix material generations for SolidReference --- .../kotlin/hep/dataforge/vision/VisionBase.kt | 26 +++++++++---------- .../dataforge/vision/solid/SolidReference.kt | 7 +++-- .../vision/solid/SolidReferenceTest.kt | 26 +++++++++++++++++++ .../vision/solid/three/ThreeMaterials.kt | 7 ++--- .../solid/three/ThreeReferenceFactory.kt | 3 +++ 5 files changed, 51 insertions(+), 18 deletions(-) create mode 100644 visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/SolidReferenceTest.kt diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt index 899c2877..b5d03eb7 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt @@ -131,8 +131,20 @@ public open class VisionBase(internal var properties: Config? = null) : Vision { } override fun update(change: VisionChange) { + change.properties?.let { + updateProperties(Name.EMPTY, it.asMetaItem()) + } + } - fun updateProperties(at: Name, item: MetaItem) { + public companion object { + public val descriptor: NodeDescriptor = NodeDescriptor { + value(STYLE_KEY) { + type(ValueType.STRING) + multiple = true + } + } + + public fun Vision.updateProperties(at: Name, item: MetaItem) { when (item) { is ValueItem -> { if (item.value == Null) { @@ -146,18 +158,6 @@ public open class VisionBase(internal var properties: Config? = null) : Vision { } } - change.properties?.let { - updateProperties(Name.EMPTY, it.asMetaItem()) - } - } - - public companion object { - public val descriptor: NodeDescriptor = NodeDescriptor { - value(STYLE_KEY) { - type(ValueType.STRING) - multiple = true - } - } } } diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidReference.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidReference.kt index 261f16d8..e49d8d0f 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidReference.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidReference.kt @@ -3,6 +3,7 @@ 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.serialization.SerialName @@ -99,7 +100,7 @@ public class SolidReferenceGroup( ReferenceChild(childName + key.asName()) } ?: emptyMap() - override val meta: Meta get() =TODO()// getChildProperty(childName, Name.EMPTY).node ?: Meta.EMPTY + override val meta: Meta get() = TODO()// getChildProperty(childName, Name.EMPTY).node ?: Meta.EMPTY override fun getOwnProperty(name: Name): MetaItem? = getChildProperty(childName, name) @@ -136,7 +137,9 @@ public class SolidReferenceGroup( } override fun update(change: VisionChange) { - TODO("Not yet implemented") + change.properties?.let { + updateProperties(Name.EMPTY, it.asMetaItem()) + } } override val descriptor: NodeDescriptor get() = prototype.descriptor diff --git a/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/SolidReferenceTest.kt b/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/SolidReferenceTest.kt new file mode 100644 index 00000000..8b3c85e0 --- /dev/null +++ b/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/SolidReferenceTest.kt @@ -0,0 +1,26 @@ +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 { + SolidMaterial.MATERIAL_COLOR_KEY put "red" + } + ref("test", Box(100f,100f,100f).apply { + color("blue") + useStyle(referenceStyle) + }) + } + + + @Test + fun testReferenceProperty(){ + assertEquals("blue", (groupWithReference["test"] as Solid).color.string) + } +} \ No newline at end of file diff --git a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeMaterials.kt b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeMaterials.kt index 2d459217..0403fdb9 100644 --- a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeMaterials.kt +++ b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeMaterials.kt @@ -8,7 +8,6 @@ import hep.dataforge.values.string import hep.dataforge.vision.Colors import hep.dataforge.vision.Vision import hep.dataforge.vision.allStyles -import hep.dataforge.vision.merge import hep.dataforge.vision.solid.SolidMaterial import info.laht.threekt.materials.LineBasicMaterial import info.laht.threekt.materials.Material @@ -143,6 +142,7 @@ public fun Mesh.updateMaterial(vision: Vision) { includeStyles = false, includeDefaults = false ) + material = when { ownMaterialMeta == null && stylesMaterialMeta == null && parentMaterialMeta == null -> { //use default is not material properties are defined @@ -153,8 +153,9 @@ public fun Mesh.updateMaterial(vision: Vision) { ThreeMaterials.cacheMeta(stylesMaterialMeta.node ?: Meta.EMPTY) } else -> { - val merge = sequenceOf(ownMaterialMeta, stylesMaterialMeta, parentMaterialMeta).merge().node ?: Meta.EMPTY - ThreeMaterials.buildMaterial(merge) + vision.getProperty(SolidMaterial.MATERIAL_KEY).node?.let { + ThreeMaterials.buildMaterial(it) + } ?: ThreeMaterials.DEFAULT } } } diff --git a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeReferenceFactory.kt b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeReferenceFactory.kt index 8ccbc37f..a1a98572 100644 --- a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeReferenceFactory.kt +++ b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeReferenceFactory.kt @@ -1,9 +1,11 @@ package hep.dataforge.vision.solid.three +import hep.dataforge.meta.node import hep.dataforge.names.cutFirst import hep.dataforge.names.firstOrNull import hep.dataforge.names.toName import hep.dataforge.vision.solid.Solid +import hep.dataforge.vision.solid.SolidMaterial import hep.dataforge.vision.solid.SolidReferenceGroup import hep.dataforge.vision.solid.SolidReferenceGroup.Companion.REFERENCE_CHILD_PROPERTY_PREFIX import info.laht.threekt.core.BufferGeometry @@ -40,6 +42,7 @@ public object ThreeReferenceFactory : ThreeFactory { object3D.updatePosition(obj) if(object3D is Mesh){ + //object3D.material = ThreeMaterials.buildMaterial(obj.getProperty(SolidMaterial.MATERIAL_KEY).node!!) object3D.applyProperties(obj) } From 1e183107cf9b8bc63a6ef7b02a1919a2d3ed8450 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 26 Dec 2020 17:48:18 +0300 Subject: [PATCH 17/19] Properly treat prototype styles in editor --- .../dataforge/vision/bootstrap/visionPropertyEditor.kt | 7 ++++++- .../hep/dataforge/vision/react/PropertyEditor.kt | 9 +++++---- .../kotlin/hep/dataforge/vision/react/valueChooser.kt | 10 +++++----- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/ui/bootstrap/src/main/kotlin/hep/dataforge/vision/bootstrap/visionPropertyEditor.kt b/ui/bootstrap/src/main/kotlin/hep/dataforge/vision/bootstrap/visionPropertyEditor.kt index b9a30bc8..2d8b96f0 100644 --- a/ui/bootstrap/src/main/kotlin/hep/dataforge/vision/bootstrap/visionPropertyEditor.kt +++ b/ui/bootstrap/src/main/kotlin/hep/dataforge/vision/bootstrap/visionPropertyEditor.kt @@ -4,6 +4,7 @@ import hep.dataforge.meta.descriptors.NodeDescriptor import hep.dataforge.vision.* import hep.dataforge.vision.react.metaViewer import hep.dataforge.vision.react.propertyEditor + import hep.dataforge.vision.solid.SolidReference import org.w3c.dom.Element import react.RBuilder import react.dom.render @@ -22,7 +23,11 @@ public fun RBuilder.visionPropertyEditor( descriptor = descriptor, key = key) } - val styles = vision.styles + val styles = if (vision is SolidReference) { + (vision.styles + vision.prototype.styles).distinct() + } else { + vision.styles + } if (styles.isNotEmpty()) { card("Styles") { accordion("styles") { diff --git a/ui/react/src/main/kotlin/hep/dataforge/vision/react/PropertyEditor.kt b/ui/react/src/main/kotlin/hep/dataforge/vision/react/PropertyEditor.kt index c4eb03a0..3eb1eb97 100644 --- a/ui/react/src/main/kotlin/hep/dataforge/vision/react/PropertyEditor.kt +++ b/ui/react/src/main/kotlin/hep/dataforge/vision/react/PropertyEditor.kt @@ -65,11 +65,12 @@ private val PropertyEditorItem: FunctionalComponent = private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) { var expanded: Boolean by useState { true } - val itemName by useState { props.name ?: Name.EMPTY } + val itemName = useMemo( { props.name ?: Name.EMPTY }, arrayOf(props.name)) var item: MetaItem? by useState { props.provider.getItem(itemName) } - val descriptorItem: ItemDescriptor? = props.descriptor?.get(itemName) + val descriptorItem: ItemDescriptor? = + useMemo({ props.descriptor?.get(itemName) }, arrayOf(props.descriptor, itemName)) - if(descriptorItem?.hidden == true) return //fail fast for hidden property + if (descriptorItem?.hidden == true) return //fail fast for hidden property var actualItem: MetaItem? by useState { item ?: props.defaultProvider?.getItem(itemName) ?: descriptorItem?.defaultItem() @@ -79,7 +80,7 @@ private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) { fun update() { item = props.provider.getItem(itemName) - actualItem = item ?: descriptorItem?.defaultItem() + actualItem = item ?: props.defaultProvider?.getItem(itemName) ?: descriptorItem?.defaultItem() } if (props.updateFlow != null) { diff --git a/ui/react/src/main/kotlin/hep/dataforge/vision/react/valueChooser.kt b/ui/react/src/main/kotlin/hep/dataforge/vision/react/valueChooser.kt index 884791b5..68638e25 100644 --- a/ui/react/src/main/kotlin/hep/dataforge/vision/react/valueChooser.kt +++ b/ui/react/src/main/kotlin/hep/dataforge/vision/react/valueChooser.kt @@ -20,17 +20,17 @@ import styled.styledInput import styled.styledSelect public external interface ValueChooserProps : RProps { - var item: MetaItem? - var descriptor: ValueDescriptor? - var valueChanged: ((Value?) -> Unit)? + public var item: MetaItem? + public var descriptor: ValueDescriptor? + public var valueChanged: ((Value?) -> Unit)? } public external interface ValueChooserState : RState { - var rawInput: Boolean? + public var rawInput: Boolean? } @JsExport -class ValueChooserComponent(props: ValueChooserProps) : RComponent(props) { +public class ValueChooserComponent(props: ValueChooserProps) : RComponent(props) { private val element = createRef() private fun getValue(): Value? { From 56481933ed287fba3e995496352d5d67f90b1e9a Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 26 Dec 2020 22:46:01 +0300 Subject: [PATCH 18/19] Static export fix --- .../hep/dataforge/vision/solid/simpleCube.kt | 2 +- .../hep/dataforge/vision/solid/stars.kt | 37 +++ .../dataforge/vision/react/PropertyEditor.kt | 7 +- .../dataforge/vision/react/valueChooser.kt | 222 ++++++++++-------- .../kotlin/hep/dataforge/vision/Vision.kt | 2 + .../vision/html/VisionTagConsumer.kt | 4 +- .../dataforge/vision/html/staticHtmlRender.kt | 3 +- .../kotlin/hep/dataforge/vision/headers.kt | 14 +- .../kotlin/hep/dataforge/vision/htmlExport.kt | 6 +- .../hep/dataforge/vision/solid/FX3DPlugin.kt | 2 +- .../hep/dataforge/vision/solid/Solid.kt | 10 +- .../hep/dataforge/vision/solid/SolidBase.kt | 2 +- .../dataforge/vision/solid/SolidManager.kt | 2 +- .../dataforge/vision/solid/SolidMaterial.kt | 4 +- .../vision/solid/specifications/Axes.kt | 7 +- .../vision/solid/three/ThreeCanvas.kt | 7 +- .../vision/solid/three/ThreeFactory.kt | 2 +- .../vision/solid/three/ThreePlugin.kt | 12 +- .../vision/three/server/serverExtensions.kt | 6 +- 19 files changed, 211 insertions(+), 140 deletions(-) create mode 100644 playground/src/jvmMain/kotlin/hep/dataforge/vision/solid/stars.kt diff --git a/playground/src/jvmMain/kotlin/hep/dataforge/vision/solid/simpleCube.kt b/playground/src/jvmMain/kotlin/hep/dataforge/vision/solid/simpleCube.kt index f4558c23..215e742c 100644 --- a/playground/src/jvmMain/kotlin/hep/dataforge/vision/solid/simpleCube.kt +++ b/playground/src/jvmMain/kotlin/hep/dataforge/vision/solid/simpleCube.kt @@ -17,5 +17,5 @@ fun main() { } } - fragment.makeFile(resourceLocation = ResourceLocation.LOCAL) + fragment.makeFile(resourceLocation = ResourceLocation.SYSTEM) } \ No newline at end of file diff --git a/playground/src/jvmMain/kotlin/hep/dataforge/vision/solid/stars.kt b/playground/src/jvmMain/kotlin/hep/dataforge/vision/solid/stars.kt new file mode 100644 index 00000000..dcfd8bcc --- /dev/null +++ b/playground/src/jvmMain/kotlin/hep/dataforge/vision/solid/stars.kt @@ -0,0 +1,37 @@ +package hep.dataforge.vision.solid + +import hep.dataforge.meta.DFExperimental +import hep.dataforge.vision.ResourceLocation +import hep.dataforge.vision.VisionManager +import hep.dataforge.vision.html.fragment +import hep.dataforge.vision.three.server.makeFile +import hep.dataforge.vision.three.server.solid +import kotlinx.html.h1 +import java.nio.file.Paths +import kotlin.random.Random + +@OptIn(DFExperimental::class) +fun main() { + val random = Random(112233) + val fragment = VisionManager.fragment { + h1 { +"Happy new year!" } + vision { + solid { + repeat(100) { + sphere(5) { + x = random.nextDouble(-300.0, 300.0) + y = random.nextDouble(-300.0, 300.0) + z = random.nextDouble(-300.0, 300.0) + material { + color(random.nextInt()) + specularColor(random.nextInt()) + } + detail = 16 + } + } + } + } + } + + fragment.makeFile(Paths.get("stars.html"), resourceLocation = ResourceLocation.EMBED) +} \ No newline at end of file diff --git a/ui/react/src/main/kotlin/hep/dataforge/vision/react/PropertyEditor.kt b/ui/react/src/main/kotlin/hep/dataforge/vision/react/PropertyEditor.kt index 3eb1eb97..bcc27f68 100644 --- a/ui/react/src/main/kotlin/hep/dataforge/vision/react/PropertyEditor.kt +++ b/ui/react/src/main/kotlin/hep/dataforge/vision/react/PropertyEditor.kt @@ -32,7 +32,7 @@ public external interface PropertyEditorProps : RProps { public var provider: MutableItemProvider /** - * Provide default item (greyed out if used + * Provide default item (greyed out if used) */ public var defaultProvider: ItemProvider? @@ -65,11 +65,12 @@ private val PropertyEditorItem: FunctionalComponent = private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) { var expanded: Boolean by useState { true } - val itemName = useMemo( { props.name ?: Name.EMPTY }, arrayOf(props.name)) - var item: MetaItem? by useState { props.provider.getItem(itemName) } + val itemName = props.name ?: Name.EMPTY val descriptorItem: ItemDescriptor? = useMemo({ props.descriptor?.get(itemName) }, arrayOf(props.descriptor, itemName)) + var item: MetaItem? by useState { props.provider.getItem(itemName) } + if (descriptorItem?.hidden == true) return //fail fast for hidden property var actualItem: MetaItem? by useState { diff --git a/ui/react/src/main/kotlin/hep/dataforge/vision/react/valueChooser.kt b/ui/react/src/main/kotlin/hep/dataforge/vision/react/valueChooser.kt index 68638e25..86af2e4c 100644 --- a/ui/react/src/main/kotlin/hep/dataforge/vision/react/valueChooser.kt +++ b/ui/react/src/main/kotlin/hep/dataforge/vision/react/valueChooser.kt @@ -9,132 +9,156 @@ import hep.dataforge.vision.widgetType import kotlinx.html.InputType import kotlinx.html.js.onChangeFunction import kotlinx.html.js.onKeyDownFunction -import org.w3c.dom.HTMLElement import org.w3c.dom.HTMLInputElement import org.w3c.dom.HTMLSelectElement import org.w3c.dom.events.Event import react.* -import react.dom.defaultValue import react.dom.option import styled.styledInput import styled.styledSelect public external interface ValueChooserProps : RProps { - public var item: MetaItem? + public var item: MetaItem? public var descriptor: ValueDescriptor? public var valueChanged: ((Value?) -> Unit)? } -public external interface ValueChooserState : RState { - public var rawInput: Boolean? -} +@JsExport +public val StringValueChooser: FunctionalComponent = + functionalComponent("StringValueChooser") { props -> + var value by useState(props.item.string ?: "") + val keyDown: (Event) -> Unit = { event -> + if (event.type == "keydown" && event.asDynamic().key == "Enter") { + value = (event.target as HTMLInputElement).value + if(value!= props.item.string) { + props.valueChanged?.invoke(value.asValue()) + } + } + } + val handleChange: (Event) -> Unit = { + value = (it.target as HTMLInputElement).value + } + styledInput(type = InputType.text) { + attrs { + this.value = value + onKeyDownFunction = keyDown + onChangeFunction = handleChange + } + } + } @JsExport -public class ValueChooserComponent(props: ValueChooserProps) : RComponent(props) { - private val element = createRef() - - private fun getValue(): Value? { - val element = element.current ?: return null//state.element ?: return null - return when (element) { - is HTMLInputElement -> if (element.type == "checkbox") { - if (element.checked) True else False - } else { - element.value.asValue() +public val BooleanValueChooser: FunctionalComponent = + functionalComponent("BooleanValueChooser") { props -> + var checkedValue by useState(props.item.boolean ?: false) + val handleChange: (Event) -> Unit = { + val newValue = (it.target as HTMLInputElement).checked + checkedValue = newValue + props.valueChanged?.invoke(newValue.asValue()) + } + styledInput(type = InputType.checkBox) { + attrs { + this.attributes["indeterminate"] = (checkedValue == null).toString() + checked = checkedValue + onChangeFunction = handleChange } - is HTMLSelectElement -> element.value.asValue() - else -> error("Unknown event target: $element") } } - private val commit: (Event) -> Unit = { _ -> - props.valueChanged?.invoke(getValue()) - } - - private val keyDown: (Event) -> Unit = { event -> - if (event.type == "keydown" && event.asDynamic().key == "Enter") { - commit(event) +@JsExport +public val NumberValueChooser: FunctionalComponent = + functionalComponent("NumberValueChooser") { props -> + var value by useState(props.item.string ?: "") + val keyDown: (Event) -> Unit = { event -> + if (event.type == "keydown" && event.asDynamic().key == "Enter") { + value = (event.target as HTMLInputElement).value + val number = value.toDoubleOrNull() + if (number == null) { + console.error("The input value $value is not a number") + } else { + props.valueChanged?.invoke(number.asValue()) + } + } + } + val handleChange: (Event) -> Unit = { + value = (it.target as HTMLInputElement).value + } + styledInput(type = InputType.number) { + attrs { + this.value = value + onKeyDownFunction = keyDown + onChangeFunction = handleChange + props.descriptor?.attributes?.get("step").string?.let { + step = it + } + props.descriptor?.attributes?.get("min").string?.let { + min = it + } + props.descriptor?.attributes?.get("max").string?.let { + max = it + } + } } } - override fun shouldComponentUpdate( - nextProps: ValueChooserProps, - nextState: ValueChooserState - ): Boolean = nextProps.item !== props.item - - override fun componentDidUpdate(prevProps: ValueChooserProps, prevState: ValueChooserState, snapshot: Any) { - (element.current as? HTMLInputElement)?.let { element -> - if (element.type == "checkbox") { - element.defaultChecked = props.item?.boolean ?: false - } else { - element.defaultValue = props.item?.string ?: "" +@JsExport +public val ComboValueChooser: FunctionalComponent = + functionalComponent("ComboValueChooser") { props -> + var selected by useState(props.item.string ?: "") + val handleChange: (Event) -> Unit = { + selected = (it.target as HTMLSelectElement).value + props.valueChanged?.invoke(selected.asValue()) + } + styledSelect { + props.descriptor?.allowedValues?.forEach { + option { + +it.string + } + } + attrs { + this.value = props.item?.string ?: "" + multiple = false + onChangeFunction = handleChange } - element.indeterminate = props.item == null } } - private fun RBuilder.stringInput() = styledInput(type = InputType.text) { - attrs { - this.defaultValue = props.item?.string ?: "" - onKeyDownFunction = keyDown +@JsExport +public val ColorValueChooser: FunctionalComponent = + functionalComponent("ColorValueChooser") { props -> + var value by useState( + props.item.value?.let { value -> + if (value.type == ValueType.NUMBER) Colors.rgbToString(value.int) + else value.string + } ?: "#000000" + ) + val handleChange: (Event) -> Unit = { + value = (it.target as HTMLInputElement).value + props.valueChanged?.invoke(value.asValue()) + } + styledInput(type = InputType.color) { + attrs { + this.value = value + onChangeFunction = handleChange + } } - ref = element } - override fun RBuilder.render() { - val descriptor = props.descriptor - val type = descriptor?.type?.firstOrNull() - when { - state.rawInput == true -> stringInput() - descriptor?.widgetType == "color" -> styledInput(type = InputType.color) { - ref = element - attrs { - this.defaultValue = props.item?.value?.let { value -> - if (value.type == ValueType.NUMBER) Colors.rgbToString(value.int) - else value.string - } ?: "#000000" - onChangeFunction = commit - } - } - type == ValueType.BOOLEAN -> { - styledInput(type = InputType.checkBox) { - ref = element - attrs { - defaultChecked = props.item?.boolean ?: false - onChangeFunction = commit - } - } - } - type == ValueType.NUMBER -> styledInput(type = InputType.number) { - ref = element - attrs { - descriptor.attributes["step"].string?.let { - step = it - } - descriptor.attributes["min"].string?.let { - min = it - } - descriptor.attributes["max"].string?.let { - max = it - } - defaultValue = props.item?.string ?: "" - onKeyDownFunction = keyDown - } - } - descriptor?.allowedValues?.isNotEmpty() ?: false -> styledSelect { - descriptor!!.allowedValues.forEach { - option { - +it.string - } - } - ref = element - attrs { - this.value = props.item?.string ?: "" - multiple = false - onChangeFunction = commit - } - } - else -> stringInput() - } +@JsExport +public val ValueChooser: FunctionalComponent = functionalComponent("ValueChooser") { props -> + val rawInput by useState(false) + + val descriptor = props.descriptor + val type = descriptor?.type?.firstOrNull() + + when { + rawInput -> child(StringValueChooser, props) + descriptor?.widgetType == "color" -> child(ColorValueChooser, props) + type == ValueType.BOOLEAN -> child(BooleanValueChooser, props) + type == ValueType.NUMBER -> child(NumberValueChooser, props) + descriptor?.allowedValues?.isNotEmpty() ?: false -> child(ComboValueChooser, props) + //TODO handle lists + else -> child(StringValueChooser, props) } } @@ -142,9 +166,9 @@ internal fun RBuilder.valueChooser( name: Name, item: MetaItem?, descriptor: ValueDescriptor? = null, - callback: (Value?) -> Unit + callback: (Value?) -> Unit, ) { - child(ValueChooserComponent::class) { + child(ValueChooser) { attrs { key = name.toString() this.item = item diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt index 836d6f26..4e3b65a1 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt @@ -1,5 +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 @@ -73,6 +74,7 @@ public interface Vision : Described { * 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 get() = callbackFlow { diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/html/VisionTagConsumer.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/html/VisionTagConsumer.kt index 101bef5b..5d0a7be7 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/html/VisionTagConsumer.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/html/VisionTagConsumer.kt @@ -73,7 +73,7 @@ public abstract class VisionTagConsumer( @OptIn(DFExperimental::class) public inline fun TagConsumer.vision( - name: String, + name: String = DEFAULT_VISION_NAME, visionProvider: VisionOutput.() -> Vision, ): T = vision(name.toName(), visionProvider) @@ -103,5 +103,7 @@ public abstract class VisionTagConsumer( public const val OUTPUT_NAME_ATTRIBUTE: String = "data-output-name" public const val OUTPUT_ENDPOINT_ATTRIBUTE: String = "data-output-endpoint" public const val DEFAULT_ENDPOINT: String = "." + + public const val DEFAULT_VISION_NAME = "vision" } } \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/html/staticHtmlRender.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/html/staticHtmlRender.kt index 1ecbdb8a..5a275e82 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/html/staticHtmlRender.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/html/staticHtmlRender.kt @@ -18,9 +18,10 @@ public fun FlowContent.embedVisionFragment( val consumer = object : VisionTagConsumer(consumer, idPrefix) { override fun DIV.renderVision(name: Name, vision: Vision, outputMeta: Meta) { script { + type = "text/json" attributes["class"] = OUTPUT_DATA_CLASS unsafe { - +manager.encodeToString(vision) + +"\n${manager.encodeToString(vision)}\n" } } } diff --git a/visionforge-core/src/jvmMain/kotlin/hep/dataforge/vision/headers.kt b/visionforge-core/src/jvmMain/kotlin/hep/dataforge/vision/headers.kt index 80e5b250..7fe87dbb 100644 --- a/visionforge-core/src/jvmMain/kotlin/hep/dataforge/vision/headers.kt +++ b/visionforge-core/src/jvmMain/kotlin/hep/dataforge/vision/headers.kt @@ -35,7 +35,7 @@ public enum class ResourceLocation { EMBED } -internal const val DATAFORGE_ASSETS_PATH = ".dataforge/assets" +internal const val VISIONFORGE_ASSETS_PATH = ".dataforge/vision/assets" /** @@ -43,14 +43,14 @@ internal const val DATAFORGE_ASSETS_PATH = ".dataforge/assets" * @param */ internal fun checkOrStoreFile(basePath: Path, filePath: Path, resource: String): Path { - val fullPath = basePath.resolveSibling(filePath).toAbsolutePath() + val fullPath = basePath.resolveSibling(filePath).toAbsolutePath().resolve(resource) if (Files.exists(fullPath)) { //TODO checksum } else { //TODO add logging - val bytes = VisionManager::class.java.getResourceAsStream(resource).readAllBytes() + val bytes = VisionManager::class.java.getResourceAsStream("/$resource").readAllBytes() Files.createDirectories(fullPath.parent) Files.write(fullPath, bytes, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE) } @@ -58,7 +58,7 @@ internal fun checkOrStoreFile(basePath: Path, filePath: Path, resource: String): return if (basePath.isAbsolute && fullPath.startsWith(basePath)) { basePath.relativize(fullPath) } else { - filePath + fullPath } } @@ -78,7 +78,7 @@ internal fun embedScriptHeader(resource: String): HtmlFragment = { script { type = "text/javascript" unsafe { - val bytes = VisionManager::class.java.getResourceAsStream(resource).readAllBytes() + val bytes = VisionManager::class.java.getResourceAsStream("/$resource").readAllBytes() +bytes.toString(Charsets.UTF_8) } } @@ -108,12 +108,12 @@ public fun Context.Companion.scriptHeader( val targetPath = when (resourceLocation) { ResourceLocation.LOCAL -> checkOrStoreFile( basePath, - Path.of(DATAFORGE_ASSETS_PATH), + Path.of(VISIONFORGE_ASSETS_PATH), scriptResource ) ResourceLocation.SYSTEM -> checkOrStoreFile( Path.of("."), - Path.of(System.getProperty("user.home")).resolve(DATAFORGE_ASSETS_PATH), + Path.of(System.getProperty("user.home")).resolve(VISIONFORGE_ASSETS_PATH), scriptResource ) ResourceLocation.EMBED -> null diff --git a/visionforge-core/src/jvmMain/kotlin/hep/dataforge/vision/htmlExport.kt b/visionforge-core/src/jvmMain/kotlin/hep/dataforge/vision/htmlExport.kt index f7539a52..6ff99bcb 100644 --- a/visionforge-core/src/jvmMain/kotlin/hep/dataforge/vision/htmlExport.kt +++ b/visionforge-core/src/jvmMain/kotlin/hep/dataforge/vision/htmlExport.kt @@ -20,8 +20,10 @@ public fun HtmlVisionFragment.makeFile( title: String = "VisionForge page", show: Boolean = true, ) { - val actualFile = path ?: Files.createTempFile("tempPlot", ".html") - Files.createDirectories(actualFile.parent) + val actualFile = path?.let { + Path.of(System.getProperty("user.home")).resolve(path) + } ?: Files.createTempFile("tempPlot", ".html") + //Files.createDirectories(actualFile.parent) val htmlString = createHTML().apply { head { meta { diff --git a/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/FX3DPlugin.kt b/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/FX3DPlugin.kt index 0032f83a..35cd6806 100644 --- a/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/FX3DPlugin.kt +++ b/visionforge-fx/src/main/kotlin/hep/dataforge/vision/solid/FX3DPlugin.kt @@ -130,7 +130,7 @@ class FX3DPlugin : AbstractPlugin() { } companion object : PluginFactory { - override val tag = PluginTag("visual.fx3D", PluginTag.DATAFORGE_GROUP) + override val tag = PluginTag("vision.fx3D", PluginTag.DATAFORGE_GROUP) override val type = FX3DPlugin::class override fun invoke(meta: Meta, context: Context) = FX3DPlugin() } diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Solid.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Solid.kt index 61710713..1050dff1 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Solid.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/Solid.kt @@ -44,13 +44,13 @@ public interface Solid : Vision { public val Y_POSITION_KEY: Name = POSITION_KEY + Y_KEY public val Z_POSITION_KEY: Name = POSITION_KEY + Z_KEY - public val ROTATION: Name = "rotation".asName() + public val ROTATION_KEY: Name = "rotation".asName() - public val X_ROTATION_KEY: Name = ROTATION + X_KEY - public val Y_ROTATION_KEY: Name = ROTATION + Y_KEY - public val Z_ROTATION_KEY: Name = ROTATION + Z_KEY + public val X_ROTATION_KEY: Name = ROTATION_KEY + X_KEY + public val Y_ROTATION_KEY: Name = ROTATION_KEY + Y_KEY + public val Z_ROTATION_KEY: Name = ROTATION_KEY + Z_KEY - public val ROTATION_ORDER_KEY: Name = ROTATION + "order" + public val ROTATION_ORDER_KEY: Name = ROTATION_KEY + "order" public val SCALE_KEY: Name = "scale".asName() diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidBase.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidBase.kt index b9471724..21c8e5bf 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidBase.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidBase.kt @@ -35,6 +35,6 @@ internal fun Meta.toVector(default: Float = 0f) = Point3D( internal fun Solid.updatePosition(meta: Meta?) { meta[Solid.POSITION_KEY].node?.toVector()?.let { position = it } - meta[Solid.ROTATION].node?.toVector()?.let { rotation = it } + meta[Solid.ROTATION_KEY].node?.toVector()?.let { rotation = it } meta[Solid.SCALE_KEY].node?.toVector(1f)?.let { scale = it } } \ No newline at end of file diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidManager.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidManager.kt index fa67fe5c..e91757a4 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidManager.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidManager.kt @@ -33,7 +33,7 @@ public class SolidManager(meta: Meta) : AbstractPlugin(meta) { } public companion object : PluginFactory { - override val tag: PluginTag = PluginTag(name = "visual.spatial", group = PluginTag.DATAFORGE_GROUP) + override val tag: PluginTag = PluginTag(name = "vision.solid", group = PluginTag.DATAFORGE_GROUP) override val type: KClass = SolidManager::class override fun invoke(meta: Meta, context: Context): SolidManager = SolidManager(meta) diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidMaterial.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidMaterial.kt index 314ea57e..3062b0ad 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidMaterial.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidMaterial.kt @@ -19,12 +19,12 @@ public class SolidMaterial : Scheme() { /** * Primary web-color for the material */ - public var color: ColorAccessor = ColorAccessor(this, COLOR_KEY) + public val color: ColorAccessor = ColorAccessor(this, COLOR_KEY) /** * Specular color for phong material */ - public var specularColor: ColorAccessor = ColorAccessor(this, SPECULAR_COLOR_KEY) + public val specularColor: ColorAccessor = ColorAccessor(this, SPECULAR_COLOR_KEY) /** * Opacity diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/specifications/Axes.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/specifications/Axes.kt index 022fd028..802bebd6 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/specifications/Axes.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/specifications/Axes.kt @@ -1,9 +1,12 @@ package hep.dataforge.vision.solid.specifications -import hep.dataforge.meta.* +import hep.dataforge.meta.Scheme +import hep.dataforge.meta.SchemeSpec +import hep.dataforge.meta.boolean +import hep.dataforge.meta.double public class Axes : Scheme() { - public var visible: Boolean by boolean(rootNode?.isEmpty() != false) + public var visible: Boolean by boolean(false) public var size: Double by double(AXIS_SIZE) public var width: Double by double(AXIS_WIDTH) diff --git a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeCanvas.kt b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeCanvas.kt index a17ab549..d58fba37 100644 --- a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeCanvas.kt +++ b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeCanvas.kt @@ -20,6 +20,7 @@ import info.laht.threekt.external.controls.OrbitControls import info.laht.threekt.external.controls.TrackballControls import info.laht.threekt.geometries.EdgesGeometry import info.laht.threekt.helpers.AxesHelper +import info.laht.threekt.lights.AmbientLight import info.laht.threekt.materials.LineBasicMaterial import info.laht.threekt.math.Vector2 import info.laht.threekt.objects.LineSegments @@ -50,8 +51,11 @@ public class ThreeCanvas( public var axes: AxesHelper = AxesHelper(options.axes.size.toInt()).apply { visible = options.axes.visible } private set + private val light = AmbientLight(0x404040) + private val scene: Scene = Scene().apply { add(axes) + add(light) } public var camera: PerspectiveCamera = buildCamera(options.camera) @@ -66,6 +70,7 @@ public class ThreeCanvas( } private val canvas = (renderer.domElement as HTMLCanvasElement).apply { + className += "three-canvas" width = 600 height = 600 style.apply { @@ -131,7 +136,7 @@ public class ThreeCanvas( } internal fun attach(element: Element) { - check(element.children.length == 0){"The element for Three canvas is not empty"} + check(element.getElementsByClassName("three-canvas").length == 0){"Three canvas already created in this element"} element.appendChild(canvas) updateSize() } diff --git a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeFactory.kt b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeFactory.kt index 30fa1422..9113dbc8 100644 --- a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeFactory.kt +++ b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeFactory.kt @@ -49,7 +49,7 @@ public fun Object3D.updateProperty(source: Vision, propertyName: Name) { updateMaterialProperty(source, propertyName) } else if ( propertyName.startsWith(Solid.POSITION_KEY) - || propertyName.startsWith(Solid.ROTATION) + || propertyName.startsWith(Solid.ROTATION_KEY) || propertyName.startsWith(Solid.SCALE_KEY) ) { //update position of mesh using this object diff --git a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreePlugin.kt b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreePlugin.kt index eaadc56a..080a71f7 100644 --- a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreePlugin.kt +++ b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreePlugin.kt @@ -70,7 +70,7 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer { obj.onPropertyChange(updateScope) { name -> if ( name.startsWith(Solid.POSITION_KEY) || - name.startsWith(Solid.ROTATION) || + name.startsWith(Solid.ROTATION_KEY) || name.startsWith(Solid.SCALE_KEY) ) { //update position of mesh using this object @@ -81,14 +81,6 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer { } obj.structureChanges.onEach { (nameToken, _, child) -> -// if (name.isEmpty()) { -// logger.error { "Children change with empty name on $group" } -// return@onChildrenChange -// } - -// val parentName = name.cutLast() -// val childName = name.last()!! - //removing old object findChild(nameToken.asName())?.let { oldChild -> oldChild.parent?.remove(oldChild) @@ -153,7 +145,7 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer { } public companion object : PluginFactory { - override val tag: PluginTag = PluginTag("visual.three", PluginTag.DATAFORGE_GROUP) + override val tag: PluginTag = PluginTag("vision.threejs", PluginTag.DATAFORGE_GROUP) override val type: KClass = ThreePlugin::class override fun invoke(meta: Meta, context: Context): ThreePlugin = ThreePlugin() } diff --git a/visionforge-threejs/visionforge-threejs-server/src/jvmMain/kotlin/hep/dataforge/vision/three/server/serverExtensions.kt b/visionforge-threejs/visionforge-threejs-server/src/jvmMain/kotlin/hep/dataforge/vision/three/server/serverExtensions.kt index a4d84a23..d37fabc3 100644 --- a/visionforge-threejs/visionforge-threejs-server/src/jvmMain/kotlin/hep/dataforge/vision/three/server/serverExtensions.kt +++ b/visionforge-threejs/visionforge-threejs-server/src/jvmMain/kotlin/hep/dataforge/vision/three/server/serverExtensions.kt @@ -39,7 +39,9 @@ public fun HtmlVisionFragment.makeFile( resourceLocation: ResourceLocation = ResourceLocation.SYSTEM, show: Boolean = true, ) { - val actualPath = path ?: Files.createTempFile("tempPlot", ".html") - val scriptHeader = Context.scriptHeader("/js/visionforge-three.js", actualPath, resourceLocation) + val actualPath = path?.let { + Path.of(System.getProperty("user.home")).resolve(path) + } ?: Files.createTempFile("tempPlot", ".html") + val scriptHeader = Context.scriptHeader("js/visionforge-three.js", actualPath, resourceLocation) makeFile(visionManager, path = path, show = show, title = title, headers = arrayOf(scriptHeader)) } \ No newline at end of file From 51af6b9378da3a87ff397986d5be394620129c4c Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Mon, 28 Dec 2020 22:39:37 +0300 Subject: [PATCH 19/19] Refactor file generators --- .../main/kotlin/ru/mipt/npm/sat/satServer.kt | 13 +- playground/build.gradle.kts | 31 ++++- .../src/commonMain/kotlin/visionContext.kt | 2 + playground/src/jsMain/kotlin/PlayGroundApp.kt | 115 ++++++++++-------- .../solid/{stars.kt => randomSpheres.kt} | 11 +- .../vision/solid/serverExtensions.kt | 38 ++++++ .../hep/dataforge/vision/solid/simpleCube.kt | 4 +- settings.gradle.kts | 3 +- .../kotlin/hep/dataforge/vision/VisionBase.kt | 6 +- .../kotlin/hep/dataforge/vision/headers.kt | 14 +-- .../kotlin/hep/dataforge/vision/htmlExport.kt | 23 ++-- visionforge-plotly/build.gradle.kts | 20 +++ .../dataforge/vision/plotly/VisionOfPlotly.kt | 12 ++ .../dataforge/vision/plotly/PlotlyPlugin.kt | 33 +++++ .../hep/dataforge/vision/plotly/jsMain.kt | 15 +++ .../dataforge/vision/solid/SolidManager.kt | 5 + .../solid/specifications/Canvas3DOptions.kt | 1 + .../vision/solid/specifications/Light.kt | 8 ++ .../vision/solid/three/ThreeCanvas.kt | 3 +- .../threekt/helpers/HemisphereLightHelper.kt | 4 +- ...{HemiSphereLight.kt => HemisphereLight.kt} | 4 +- .../vision/three/server/visionContext.kt | 8 -- .../dataforge/vision/three/server/jsMain.kt | 40 +----- .../vision/three/server/serverExtensions.kt | 32 +---- 24 files changed, 279 insertions(+), 166 deletions(-) create mode 100644 playground/src/commonMain/kotlin/visionContext.kt rename playground/src/jvmMain/kotlin/hep/dataforge/vision/solid/{stars.kt => randomSpheres.kt} (81%) create mode 100644 playground/src/jvmMain/kotlin/hep/dataforge/vision/solid/serverExtensions.kt create mode 100644 visionforge-plotly/build.gradle.kts create mode 100644 visionforge-plotly/src/commonMain/kotlin/hep/dataforge/vision/plotly/VisionOfPlotly.kt create mode 100644 visionforge-plotly/src/jsMain/kotlin/hep/dataforge/vision/plotly/PlotlyPlugin.kt create mode 100644 visionforge-plotly/src/jsMain/kotlin/hep/dataforge/vision/plotly/jsMain.kt create mode 100644 visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/specifications/Light.kt rename visionforge-threejs/src/main/kotlin/info/laht/threekt/lights/{HemiSphereLight.kt => HemisphereLight.kt} (94%) delete mode 100644 visionforge-threejs/visionforge-threejs-server/src/commonMain/kotlin/hep/dataforge/vision/three/server/visionContext.kt diff --git a/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/satServer.kt b/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/satServer.kt index a1f0dca1..92b1b7d0 100644 --- a/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/satServer.kt +++ b/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/satServer.kt @@ -1,12 +1,11 @@ package ru.mipt.npm.sat +import hep.dataforge.context.Global import hep.dataforge.names.toName -import hep.dataforge.vision.solid.Solid -import hep.dataforge.vision.solid.clear -import hep.dataforge.vision.solid.color -import hep.dataforge.vision.solid.invoke +import hep.dataforge.vision.solid.* import hep.dataforge.vision.three.server.* +import hep.dataforge.vision.visionManager import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.delay import kotlinx.coroutines.isActive @@ -16,10 +15,14 @@ import kotlinx.html.h1 import kotlin.random.Random fun main() { + val satContext = Global.context("sat") { + plugin(SolidManager) + } + //Create a geometry val sat = visionOfSatellite(ySegments = 3) - val server = visionManager.serve { + val server = satContext.visionManager.serve { //use client library useThreeJs() //use css diff --git a/playground/build.gradle.kts b/playground/build.gradle.kts index 45d20f6d..4df93ed9 100644 --- a/playground/build.gradle.kts +++ b/playground/build.gradle.kts @@ -12,6 +12,16 @@ repositories{ } kotlin { + + js(IR) { + browser { + webpackTask { + this.outputFileName = "js/visionforge-playground.js" + } + } + binaries.executable() + } + jvm{ compilations.all { kotlinOptions.jvmTarget = "11" @@ -20,30 +30,39 @@ kotlin { useJUnitPlatform() } } - js(IR) { - browser { + + afterEvaluate { + val jsBrowserDistribution by tasks.getting + + tasks.getByName("jvmProcessResources") { + dependsOn(jsBrowserDistribution) + afterEvaluate { + from(jsBrowserDistribution) + } } - binaries.executable() } + sourceSets { - commonMain { + val commonMain by getting { dependencies { implementation(project(":visionforge-solid")) implementation(project(":visionforge-gdml")) - + implementation(project(":visionforge-plotly")) } } + val jsMain by getting{ dependencies { implementation(project(":ui:bootstrap")) + implementation(project(":visionforge-threejs")) } } val jvmMain by getting{ dependencies { + implementation(project(":visionforge-server")) implementation("com.github.Ricky12Awesome:json-schema-serialization:0.6.6") - implementation(project(":visionforge-threejs:visionforge-threejs-server")) } } } diff --git a/playground/src/commonMain/kotlin/visionContext.kt b/playground/src/commonMain/kotlin/visionContext.kt new file mode 100644 index 00000000..139597f9 --- /dev/null +++ b/playground/src/commonMain/kotlin/visionContext.kt @@ -0,0 +1,2 @@ + + diff --git a/playground/src/jsMain/kotlin/PlayGroundApp.kt b/playground/src/jsMain/kotlin/PlayGroundApp.kt index a4635a04..4a3018a0 100644 --- a/playground/src/jsMain/kotlin/PlayGroundApp.kt +++ b/playground/src/jsMain/kotlin/PlayGroundApp.kt @@ -1,58 +1,67 @@ -import hep.dataforge.Application -import hep.dataforge.startApplication -import hep.dataforge.vision.bootstrap.visionPropertyEditor -import hep.dataforge.vision.react.ThreeCanvasComponent -import hep.dataforge.vision.react.objectTree -import hep.dataforge.vision.solid.* -import hep.dataforge.vision.solid.specifications.Canvas3DOptions -import kotlinx.browser.document -import org.w3c.dom.HTMLElement -import react.RBuilder -import react.child -import react.dom.div -import react.dom.render - -fun RBuilder.threeCanvas(object3D: Solid, options: Canvas3DOptions.() -> Unit = {}) { - child(ThreeCanvasComponent) { - attrs { - this.obj = object3D - this.options = Canvas3DOptions(options) - } - } -} - -private class PlayGroundApp : Application { - - override fun start(state: Map) { - - val element = - document.getElementById("app") as? HTMLElement ?: error("Element with id 'canvas' not found on page") - - val obj = SolidGroup().apply { - box(100, 100, 100, name = "A") - group("B") { - position = Point3D(120, 0, 0) - box(100, 100, 100, name = "C") - } - } - - render(element) { - div("row") { - div("col-3") { - objectTree(obj) - } - div("col-6") { - threeCanvas(obj) - } - div("col-3") { - visionPropertyEditor(obj) - } - } - } - } +import hep.dataforge.context.Context +import hep.dataforge.context.Global +import hep.dataforge.meta.DFExperimental +import hep.dataforge.vision.client.VisionClient +import hep.dataforge.vision.client.renderAllVisions +import hep.dataforge.vision.plotly.PlotlyPlugin +import hep.dataforge.vision.solid.three.ThreePlugin +import kotlinx.browser.window +//fun RBuilder.threeCanvas(object3D: Solid, options: Canvas3DOptions.() -> Unit = {}) { +// child(ThreeCanvasComponent) { +// attrs { +// this.obj = object3D +// this.options = Canvas3DOptions(options) +// } +// } +//} +// +//private class PlayGroundApp : Application { +// +// override fun start(state: Map) { +// +// val element = +// document.getElementById("app") as? HTMLElement ?: error("Element with id 'canvas' not found on page") +// +// val obj = SolidGroup().apply { +// box(100, 100, 100, name = "A") +// group("B") { +// position = Point3D(120, 0, 0) +// box(100, 100, 100, name = "C") +// } +// } +// +// render(element) { +// div("row") { +// div("col-3") { +// objectTree(obj) +// } +// div("col-6") { +// threeCanvas(obj) +// } +// div("col-3") { +// visionPropertyEditor(obj) +// } +// } +// } +// } +// +//} + +public val visionContext: Context = Global.context("VISION") { + plugin(ThreePlugin) + plugin(PlotlyPlugin) + plugin(VisionClient) } +@DFExperimental fun main() { - startApplication(::PlayGroundApp) + //Loading three-js renderer + val clientManager = visionContext.plugins.fetch(VisionClient) + + //Fetch from server and render visions for all outputs + window.onload = { + clientManager.renderAllVisions() + } + //startApplication(::PlayGroundApp) } \ No newline at end of file diff --git a/playground/src/jvmMain/kotlin/hep/dataforge/vision/solid/stars.kt b/playground/src/jvmMain/kotlin/hep/dataforge/vision/solid/randomSpheres.kt similarity index 81% rename from playground/src/jvmMain/kotlin/hep/dataforge/vision/solid/stars.kt rename to playground/src/jvmMain/kotlin/hep/dataforge/vision/solid/randomSpheres.kt index dcfd8bcc..728714af 100644 --- a/playground/src/jvmMain/kotlin/hep/dataforge/vision/solid/stars.kt +++ b/playground/src/jvmMain/kotlin/hep/dataforge/vision/solid/randomSpheres.kt @@ -4,21 +4,20 @@ import hep.dataforge.meta.DFExperimental import hep.dataforge.vision.ResourceLocation import hep.dataforge.vision.VisionManager import hep.dataforge.vision.html.fragment -import hep.dataforge.vision.three.server.makeFile -import hep.dataforge.vision.three.server.solid import kotlinx.html.h1 import java.nio.file.Paths import kotlin.random.Random @OptIn(DFExperimental::class) fun main() { + val random = Random(112233) val fragment = VisionManager.fragment { h1 { +"Happy new year!" } vision { solid { repeat(100) { - sphere(5) { + sphere(5, name = "sphere[$it]") { x = random.nextDouble(-300.0, 300.0) y = random.nextDouble(-300.0, 300.0) z = random.nextDouble(-300.0, 300.0) @@ -33,5 +32,9 @@ fun main() { } } - fragment.makeFile(Paths.get("stars.html"), resourceLocation = ResourceLocation.EMBED) + visionContext.makeVisionFile( + fragment, + Paths.get("randomSpheres.html"), + resourceLocation = ResourceLocation.EMBED + ) } \ No newline at end of file diff --git a/playground/src/jvmMain/kotlin/hep/dataforge/vision/solid/serverExtensions.kt b/playground/src/jvmMain/kotlin/hep/dataforge/vision/solid/serverExtensions.kt new file mode 100644 index 00000000..c1f3b273 --- /dev/null +++ b/playground/src/jvmMain/kotlin/hep/dataforge/vision/solid/serverExtensions.kt @@ -0,0 +1,38 @@ +package hep.dataforge.vision.solid + +import hep.dataforge.context.Context +import hep.dataforge.context.Global +import hep.dataforge.meta.DFExperimental +import hep.dataforge.vision.ResourceLocation +import hep.dataforge.vision.VisionManager +import hep.dataforge.vision.html.HtmlVisionFragment +import hep.dataforge.vision.makeVisionFile +import hep.dataforge.vision.scriptHeader +import hep.dataforge.vision.three.server.VisionServer +import hep.dataforge.vision.three.server.useScript +import java.nio.file.Path + + +/** + * A global vision context used to resolve different vision renderers + */ +@DFExperimental +public val visionContext: Context = Global.context("VISION") { + plugin(VisionManager) + plugin(SolidManager) +} + +public fun VisionServer.usePlayground(): Unit { + useScript("js/visionforge-playground.js") +} + +@DFExperimental +public fun Context.makeVisionFile( + fragment: HtmlVisionFragment, + path: Path? = null, + title: String = "VisionForge page", + resourceLocation: ResourceLocation = ResourceLocation.SYSTEM, + show: Boolean = true, +): Unit = makeVisionFile(fragment, path = path, title = title, show = show) { actualPath -> + scriptHeader("js/visionforge-playground.js", actualPath, resourceLocation) +} diff --git a/playground/src/jvmMain/kotlin/hep/dataforge/vision/solid/simpleCube.kt b/playground/src/jvmMain/kotlin/hep/dataforge/vision/solid/simpleCube.kt index 215e742c..ca8e70b5 100644 --- a/playground/src/jvmMain/kotlin/hep/dataforge/vision/solid/simpleCube.kt +++ b/playground/src/jvmMain/kotlin/hep/dataforge/vision/solid/simpleCube.kt @@ -4,8 +4,6 @@ import hep.dataforge.meta.DFExperimental import hep.dataforge.vision.ResourceLocation import hep.dataforge.vision.VisionManager import hep.dataforge.vision.html.fragment -import hep.dataforge.vision.three.server.makeFile -import hep.dataforge.vision.three.server.solid @OptIn(DFExperimental::class) fun main() { @@ -17,5 +15,5 @@ fun main() { } } - fragment.makeFile(resourceLocation = ResourceLocation.SYSTEM) + visionContext.makeVisionFile(fragment = fragment, resourceLocation = ResourceLocation.SYSTEM) } \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 3877a947..36931914 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -33,7 +33,7 @@ rootProject.name = "visionforge" include( // ":ui", ":ui:react", - ":ui:ring", +// ":ui:ring", // ":ui:material", ":ui:bootstrap", ":visionforge-core", @@ -43,6 +43,7 @@ include( ":visionforge-threejs:visionforge-threejs-server", ":visionforge-gdml", ":visionforge-server", + ":visionforge-plotly", ":demo:spatial-showcase", ":demo:gdml", ":demo:muon-monitor", diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt index b5d03eb7..dddcb377 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt @@ -16,7 +16,6 @@ import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -import kotlinx.css.th import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.Transient @@ -27,7 +26,6 @@ internal data class PropertyListener( val action: (name: Name) -> Unit, ) - /** * A full base implementation for a [Vision] * @param properties Object own properties excluding styles and inheritance @@ -53,7 +51,7 @@ public open class VisionBase(internal var properties: Config? = null) : Vision { override val meta: Meta get() = properties ?: Meta.EMPTY @Synchronized - private fun getOrCreateConfig(): Config { + protected fun getOrCreateConfig(): Config { if (properties == null) { val newProperties = Config() newProperties.onChange(this) { name, oldItem, newItem -> @@ -91,7 +89,6 @@ public open class VisionBase(internal var properties: Config? = null) : Vision { yield(descriptor?.get(name)?.defaultItem()) }.merge() - @Synchronized override fun setProperty(name: Name, item: MetaItem?, notify: Boolean) { getOrCreateConfig().setItem(name, item) if (notify) { @@ -117,6 +114,7 @@ public open class VisionBase(internal var properties: Config? = null) : Vision { @Transient private val propertyInvalidationFlow: MutableSharedFlow = MutableSharedFlow() + @DFExperimental override val propertyChanges: Flow get() = propertyInvalidationFlow override fun onPropertyChange(scope: CoroutineScope, callback: suspend (Name) -> Unit) { diff --git a/visionforge-core/src/jvmMain/kotlin/hep/dataforge/vision/headers.kt b/visionforge-core/src/jvmMain/kotlin/hep/dataforge/vision/headers.kt index 7fe87dbb..5dfb5352 100644 --- a/visionforge-core/src/jvmMain/kotlin/hep/dataforge/vision/headers.kt +++ b/visionforge-core/src/jvmMain/kotlin/hep/dataforge/vision/headers.kt @@ -42,8 +42,8 @@ internal const val VISIONFORGE_ASSETS_PATH = ".dataforge/vision/assets" * Check if the asset exists in given local location and put it there if it does not * @param */ -internal fun checkOrStoreFile(basePath: Path, filePath: Path, resource: String): Path { - val fullPath = basePath.resolveSibling(filePath).toAbsolutePath().resolve(resource) +internal fun checkOrStoreFile(htmlPath: Path, filePath: Path, resource: String): Path { + val fullPath = htmlPath.resolveSibling(filePath).toAbsolutePath().resolve(resource) if (Files.exists(fullPath)) { //TODO checksum @@ -55,8 +55,8 @@ internal fun checkOrStoreFile(basePath: Path, filePath: Path, resource: String): Files.write(fullPath, bytes, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE) } - return if (basePath.isAbsolute && fullPath.startsWith(basePath)) { - basePath.relativize(fullPath) + return if (htmlPath.isAbsolute && fullPath.startsWith(htmlPath.parent)) { + htmlPath.parent.relativize(fullPath) } else { fullPath } @@ -100,14 +100,14 @@ internal fun fileCssHeader( * Make a script header, automatically copying file to appropriate location */ @DFExperimental -public fun Context.Companion.scriptHeader( +public fun Context.scriptHeader( scriptResource: String, - basePath: Path, + htmlPath: Path, resourceLocation: ResourceLocation, ): HtmlFragment { val targetPath = when (resourceLocation) { ResourceLocation.LOCAL -> checkOrStoreFile( - basePath, + htmlPath, Path.of(VISIONFORGE_ASSETS_PATH), scriptResource ) diff --git a/visionforge-core/src/jvmMain/kotlin/hep/dataforge/vision/htmlExport.kt b/visionforge-core/src/jvmMain/kotlin/hep/dataforge/vision/htmlExport.kt index 6ff99bcb..d560f46a 100644 --- a/visionforge-core/src/jvmMain/kotlin/hep/dataforge/vision/htmlExport.kt +++ b/visionforge-core/src/jvmMain/kotlin/hep/dataforge/vision/htmlExport.kt @@ -1,9 +1,16 @@ package hep.dataforge.vision +import hep.dataforge.context.Context import hep.dataforge.meta.DFExperimental -import hep.dataforge.vision.html.* -import kotlinx.html.* +import hep.dataforge.vision.html.HtmlFragment +import hep.dataforge.vision.html.HtmlVisionFragment +import hep.dataforge.vision.html.embedVisionFragment +import hep.dataforge.vision.html.fragment +import kotlinx.html.body +import kotlinx.html.head +import kotlinx.html.meta import kotlinx.html.stream.createHTML +import kotlinx.html.title import java.awt.Desktop import java.nio.file.Files import java.nio.file.Path @@ -13,12 +20,12 @@ import java.nio.file.Path * Make a file with the embedded vision data */ @DFExperimental -public fun HtmlVisionFragment.makeFile( - manager: VisionManager, - vararg headers: HtmlFragment, +public fun Context.makeVisionFile( + fragment: HtmlVisionFragment, path: Path? = null, title: String = "VisionForge page", show: Boolean = true, + headerBuilder: (Path) -> HtmlFragment, ) { val actualFile = path?.let { Path.of(System.getProperty("user.home")).resolve(path) @@ -28,14 +35,12 @@ public fun HtmlVisionFragment.makeFile( head { meta { charset = "utf-8" - headers.forEach { - fragment(it) - } + fragment(headerBuilder(actualFile)) } title(title) } body { - embedVisionFragment(manager, fragment = this@makeFile) + embedVisionFragment(visionManager, fragment = fragment) } }.finalize() diff --git a/visionforge-plotly/build.gradle.kts b/visionforge-plotly/build.gradle.kts new file mode 100644 index 00000000..51419b1e --- /dev/null +++ b/visionforge-plotly/build.gradle.kts @@ -0,0 +1,20 @@ +plugins { + id("ru.mipt.npm.mpp") +} + +kscience { + useSerialization() +} + +val plotlyVersion = "0.3.1-dev" + +kotlin { + sourceSets { + commonMain { + dependencies { + api(project(":visionforge-core")) + api("kscience.plotlykt:plotlykt-core:${plotlyVersion}") + } + } + } +} \ No newline at end of file diff --git a/visionforge-plotly/src/commonMain/kotlin/hep/dataforge/vision/plotly/VisionOfPlotly.kt b/visionforge-plotly/src/commonMain/kotlin/hep/dataforge/vision/plotly/VisionOfPlotly.kt new file mode 100644 index 00000000..997a560a --- /dev/null +++ b/visionforge-plotly/src/commonMain/kotlin/hep/dataforge/vision/plotly/VisionOfPlotly.kt @@ -0,0 +1,12 @@ +package hep.dataforge.vision.plotly + +import hep.dataforge.meta.DFExperimental +import hep.dataforge.vision.VisionBase +import hep.dataforge.vision.html.VisionOutput +import kscience.plotly.Plot +import kscience.plotly.Plotly + +public class VisionOfPlotly(public val plot: Plot): VisionBase(plot.config) + +@DFExperimental +public inline fun VisionOutput.plotly(block: Plot.() -> Unit): VisionOfPlotly = VisionOfPlotly(Plotly.plot(block)) \ No newline at end of file diff --git a/visionforge-plotly/src/jsMain/kotlin/hep/dataforge/vision/plotly/PlotlyPlugin.kt b/visionforge-plotly/src/jsMain/kotlin/hep/dataforge/vision/plotly/PlotlyPlugin.kt new file mode 100644 index 00000000..9e2793dc --- /dev/null +++ b/visionforge-plotly/src/jsMain/kotlin/hep/dataforge/vision/plotly/PlotlyPlugin.kt @@ -0,0 +1,33 @@ +package hep.dataforge.vision.plotly + +import hep.dataforge.context.AbstractPlugin +import hep.dataforge.context.Context +import hep.dataforge.context.PluginFactory +import hep.dataforge.context.PluginTag +import hep.dataforge.meta.Meta +import hep.dataforge.vision.Vision +import hep.dataforge.vision.client.ElementVisionRenderer +import kscience.plotly.PlotlyConfig +import kscience.plotly.plot +import org.w3c.dom.Element +import kotlin.reflect.KClass + +public class PlotlyPlugin : AbstractPlugin(), ElementVisionRenderer { + + override val tag: PluginTag get() = Companion.tag + + override fun rateVision(vision: Vision): Int = + if (vision is VisionOfPlotly) ElementVisionRenderer.DEFAULT_RATING else ElementVisionRenderer.ZERO_RATING + + override fun render(element: Element, vision: Vision, meta: Meta) { + val plot = (vision as? VisionOfPlotly)?.plot ?: error("Only VisionOfPlotly visions are supported") + val config = PlotlyConfig.read(meta) + element.plot(plot, config) + } + + public companion object : PluginFactory { + override val tag: PluginTag = PluginTag("vision.plotly", PluginTag.DATAFORGE_GROUP) + override val type: KClass = PlotlyPlugin::class + override fun invoke(meta: Meta, context: Context): PlotlyPlugin = PlotlyPlugin() + } +} \ No newline at end of file diff --git a/visionforge-plotly/src/jsMain/kotlin/hep/dataforge/vision/plotly/jsMain.kt b/visionforge-plotly/src/jsMain/kotlin/hep/dataforge/vision/plotly/jsMain.kt new file mode 100644 index 00000000..ae0ffb37 --- /dev/null +++ b/visionforge-plotly/src/jsMain/kotlin/hep/dataforge/vision/plotly/jsMain.kt @@ -0,0 +1,15 @@ +package hep.dataforge.vision.plotly + +//public fun main() { +// val visionContext: Context = Global.context("vision-client") +// +// //Loading three-js renderer +// val threePlugin = visionContext.plugins.fetch(PlotlyPlugin) +// +// val clientManager = visionContext.plugins.fetch(VisionClient) +// +// //Fetch from server and render visions for all outputs +// window.onload = { +// clientManager.renderAllVisions() +// } +//} \ No newline at end of file diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidManager.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidManager.kt index e91757a4..f55dc1f4 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidManager.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidManager.kt @@ -4,6 +4,7 @@ import hep.dataforge.context.AbstractPlugin import hep.dataforge.context.Context import hep.dataforge.context.PluginFactory import hep.dataforge.context.PluginTag +import hep.dataforge.meta.DFExperimental import hep.dataforge.meta.Meta import hep.dataforge.names.Name import hep.dataforge.names.toName @@ -12,6 +13,7 @@ import hep.dataforge.vision.VisionBase import hep.dataforge.vision.VisionGroupBase import hep.dataforge.vision.VisionManager import hep.dataforge.vision.VisionManager.Companion.VISION_SERIALIZER_MODULE_TARGET +import hep.dataforge.vision.html.VisionOutput import kotlinx.serialization.PolymorphicSerializer import kotlinx.serialization.json.Json import kotlinx.serialization.modules.PolymorphicModuleBuilder @@ -73,3 +75,6 @@ public class SolidManager(meta: Meta) : AbstractPlugin(meta) { public fun decodeFromString(str: String): Solid = jsonForSolids.decodeFromString(PolymorphicSerializer(Solid::class), str) } } + +@DFExperimental +public inline fun VisionOutput.solid(block: SolidGroup.() -> Unit): SolidGroup = SolidGroup().apply(block) \ No newline at end of file diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/specifications/Canvas3DOptions.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/specifications/Canvas3DOptions.kt index cfe59171..2a147100 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/specifications/Canvas3DOptions.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/specifications/Canvas3DOptions.kt @@ -5,6 +5,7 @@ import hep.dataforge.names.Name public class Canvas3DOptions : Scheme() { public var axes: Axes by spec(Axes) + public var light: Light by spec(Light) public var camera: Camera by spec(Camera) public var controls: Controls by spec(Controls) diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/specifications/Light.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/specifications/Light.kt new file mode 100644 index 00000000..46ba8939 --- /dev/null +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/specifications/Light.kt @@ -0,0 +1,8 @@ +package hep.dataforge.vision.solid.specifications + +import hep.dataforge.meta.Scheme +import hep.dataforge.meta.SchemeSpec + +public class Light : Scheme() { + public companion object : SchemeSpec(::Light) +} \ No newline at end of file diff --git a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeCanvas.kt b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeCanvas.kt index d58fba37..6df44691 100644 --- a/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeCanvas.kt +++ b/visionforge-threejs/src/main/kotlin/hep/dataforge/vision/solid/three/ThreeCanvas.kt @@ -51,7 +51,7 @@ public class ThreeCanvas( public var axes: AxesHelper = AxesHelper(options.axes.size.toInt()).apply { visible = options.axes.visible } private set - private val light = AmbientLight(0x404040) + private var light = buildLight(options.light) private val scene: Scene = Scene().apply { add(axes) @@ -169,6 +169,7 @@ public class ThreeCanvas( } } + private fun buildLight(spec: Light): info.laht.threekt.lights.Light = AmbientLight(0x404040) private fun buildCamera(spec: Camera) = PerspectiveCamera( spec.fov, diff --git a/visionforge-threejs/src/main/kotlin/info/laht/threekt/helpers/HemisphereLightHelper.kt b/visionforge-threejs/src/main/kotlin/info/laht/threekt/helpers/HemisphereLightHelper.kt index af7333d5..4351faa7 100644 --- a/visionforge-threejs/src/main/kotlin/info/laht/threekt/helpers/HemisphereLightHelper.kt +++ b/visionforge-threejs/src/main/kotlin/info/laht/threekt/helpers/HemisphereLightHelper.kt @@ -28,7 +28,7 @@ package info.laht.threekt.helpers import info.laht.threekt.core.Object3D -import info.laht.threekt.lights.HemiSphereLight +import info.laht.threekt.lights.HemisphereLight import info.laht.threekt.lights.Light /** @@ -39,7 +39,7 @@ import info.laht.threekt.lights.Light * @param color (optional) if this is not the set the helper will take the color of the light. */ external class HemisphereLightHelper( - light: HemiSphereLight, + light: HemisphereLight, size: Number, color: Int = definedExternally ) : Object3D { diff --git a/visionforge-threejs/src/main/kotlin/info/laht/threekt/lights/HemiSphereLight.kt b/visionforge-threejs/src/main/kotlin/info/laht/threekt/lights/HemisphereLight.kt similarity index 94% rename from visionforge-threejs/src/main/kotlin/info/laht/threekt/lights/HemiSphereLight.kt rename to visionforge-threejs/src/main/kotlin/info/laht/threekt/lights/HemisphereLight.kt index 727a36a1..c98fbd5d 100644 --- a/visionforge-threejs/src/main/kotlin/info/laht/threekt/lights/HemiSphereLight.kt +++ b/visionforge-threejs/src/main/kotlin/info/laht/threekt/lights/HemisphereLight.kt @@ -34,7 +34,7 @@ import info.laht.threekt.math.Color * * This light cannot be used to cast shadows. */ -external class HemiSphereLight( +external class HemisphereLight( skyColor: Int = definedExternally, groundColor: Int = definedExternally, intensity: Number = definedExternally @@ -42,6 +42,6 @@ external class HemiSphereLight( var groundColor: Color - fun copy(light: HemiSphereLight): HemiSphereLight + fun copy(light: HemisphereLight): HemisphereLight } \ No newline at end of file diff --git a/visionforge-threejs/visionforge-threejs-server/src/commonMain/kotlin/hep/dataforge/vision/three/server/visionContext.kt b/visionforge-threejs/visionforge-threejs-server/src/commonMain/kotlin/hep/dataforge/vision/three/server/visionContext.kt deleted file mode 100644 index c568d74a..00000000 --- a/visionforge-threejs/visionforge-threejs-server/src/commonMain/kotlin/hep/dataforge/vision/three/server/visionContext.kt +++ /dev/null @@ -1,8 +0,0 @@ -package hep.dataforge.vision.three.server - -import hep.dataforge.context.Context -import hep.dataforge.vision.VisionManager - -public expect val visionContext: Context - -public val visionManager: VisionManager get() = visionContext.plugins.fetch(VisionManager) \ No newline at end of file diff --git a/visionforge-threejs/visionforge-threejs-server/src/jsMain/kotlin/hep/dataforge/vision/three/server/jsMain.kt b/visionforge-threejs/visionforge-threejs-server/src/jsMain/kotlin/hep/dataforge/vision/three/server/jsMain.kt index f9505680..86ad4ecc 100644 --- a/visionforge-threejs/visionforge-threejs-server/src/jsMain/kotlin/hep/dataforge/vision/three/server/jsMain.kt +++ b/visionforge-threejs/visionforge-threejs-server/src/jsMain/kotlin/hep/dataforge/vision/three/server/jsMain.kt @@ -1,46 +1,18 @@ package hep.dataforge.vision.three.server -import hep.dataforge.context.Context import hep.dataforge.context.Global import hep.dataforge.vision.client.VisionClient import hep.dataforge.vision.client.renderAllVisions import hep.dataforge.vision.solid.three.ThreePlugin import kotlinx.browser.window -//FIXME check plugin loading in JS -//public actual val visionContext: Context = Global.context("vision-client") { -// //Loading three-js renderer -// plugin(ThreePlugin) -//} - -public actual val visionContext: Context = Global.context("vision-client").apply { - //Loading three-js renderer - plugins.fetch(ThreePlugin) -} - -public val clientManager: VisionClient get() = visionContext.plugins.fetch(VisionClient) - - -///** -// * Render all visions in the document using registered renderers -// */ -//@JsExport -//public fun renderVisions() { -// //Fetch from server and render visions for all outputs -// window.onload = { -// clientManager.renderAllVisions() -// } -//} -// -///** -// * Render all visions in a given element, using registered renderers -// */ -//@JsExport -//public fun renderAllVisionsAt(element: Element) { -// clientManager.renderAllVisionsAt(element) -//} - public fun main() { + //Loading three-js renderer + val visionContext = Global.context("threejs") { + plugin(ThreePlugin) + } + val clientManager = visionContext.plugins.fetch(VisionClient) + //Fetch from server and render visions for all outputs window.onload = { clientManager.renderAllVisions() diff --git a/visionforge-threejs/visionforge-threejs-server/src/jvmMain/kotlin/hep/dataforge/vision/three/server/serverExtensions.kt b/visionforge-threejs/visionforge-threejs-server/src/jvmMain/kotlin/hep/dataforge/vision/three/server/serverExtensions.kt index d37fabc3..ca20c709 100644 --- a/visionforge-threejs/visionforge-threejs-server/src/jvmMain/kotlin/hep/dataforge/vision/three/server/serverExtensions.kt +++ b/visionforge-threejs/visionforge-threejs-server/src/jvmMain/kotlin/hep/dataforge/vision/three/server/serverExtensions.kt @@ -1,47 +1,25 @@ package hep.dataforge.vision.three.server import hep.dataforge.context.Context -import hep.dataforge.context.Global import hep.dataforge.meta.DFExperimental import hep.dataforge.vision.ResourceLocation import hep.dataforge.vision.html.HtmlVisionFragment -import hep.dataforge.vision.html.VisionOutput -import hep.dataforge.vision.makeFile +import hep.dataforge.vision.makeVisionFile import hep.dataforge.vision.scriptHeader -import hep.dataforge.vision.solid.SolidGroup -import hep.dataforge.vision.solid.SolidManager -import java.nio.file.Files import java.nio.file.Path -public actual val visionContext: Context = Global.context("vision-server") { - //Loading solid manager for the backend (it does not know about three - plugin(SolidManager) -} public fun VisionServer.useThreeJs(): Unit { useScript("js/visionforge-three.js") -// header { -// script { -// unsafe { -// +"renderThreeVisions()" -// } -// } -// } } @DFExperimental -public inline fun VisionOutput.solid(block: SolidGroup.() -> Unit): SolidGroup = SolidGroup().apply(block) - -@OptIn(DFExperimental::class) -public fun HtmlVisionFragment.makeFile( +public fun Context.makeVisionFile( + fragment: HtmlVisionFragment, path: Path? = null, title: String = "VisionForge page", resourceLocation: ResourceLocation = ResourceLocation.SYSTEM, show: Boolean = true, -) { - val actualPath = path?.let { - Path.of(System.getProperty("user.home")).resolve(path) - } ?: Files.createTempFile("tempPlot", ".html") - val scriptHeader = Context.scriptHeader("js/visionforge-three.js", actualPath, resourceLocation) - makeFile(visionManager, path = path, show = show, title = title, headers = arrayOf(scriptHeader)) +): Unit = makeVisionFile(fragment, path = path, title = title, show = show) { actualPath -> + scriptHeader("js/visionforge-three.js", actualPath, resourceLocation) } \ No newline at end of file