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 } -- 2.34.1 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?> { -- 2.34.1 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 } -- 2.34.1 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() -- 2.34.1 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() } -- 2.34.1 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 -- 2.34.1 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") } } } -- 2.34.1 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 { -- 2.34.1 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 -- 2.34.1 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) -- 2.34.1 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. -- 2.34.1 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() { -- 2.34.1 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() -- 2.34.1 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) } -- 2.34.1 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 -- 2.34.1 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) } -- 2.34.1 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? { -- 2.34.1 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 -- 2.34.1 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 -- 2.34.1