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