forked from kscience/visionforge
Fixing new property update model
This commit is contained in:
parent
929832f3a5
commit
6a6d9659ca
@ -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")
|
||||
|
@ -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
|
||||
@ -76,7 +79,7 @@ internal class VariableBox(xSize: Number, ySize: Number, zSize: Number) : ThreeV
|
||||
}
|
||||
else -> mesh.updateProperty(this@VariableBox, name)
|
||||
}
|
||||
}
|
||||
}.launchIn(three.context)
|
||||
return mesh
|
||||
}
|
||||
|
||||
|
@ -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<ThreeControlsProps> = functionalCo
|
||||
if (selectedObject != null) {
|
||||
visionPropertyEditor(
|
||||
selectedObject,
|
||||
default = selectedObject.allProperties,
|
||||
default = selectedObject.describedProperties,
|
||||
key = selected
|
||||
)
|
||||
}
|
||||
|
@ -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()) {
|
||||
|
@ -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<ConfigEditorItemProps> =
|
||||
|
||||
private fun RBuilder.configEditorItem(props: ConfigEditorItemProps) {
|
||||
var expanded: Boolean by useState { true }
|
||||
var item: MetaItem<Config>? 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<Meta>? 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,
|
||||
|
@ -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<Name> = MutableSharedFlow(
|
||||
onBufferOverflow = BufferOverflow.DROP_OLDEST
|
||||
)
|
||||
@Transient
|
||||
private val _propertyInvalidationFlow: MutableSharedFlow<Name> = MutableSharedFlow()
|
||||
|
||||
override val propertyInvalidated: SharedFlow<Name> get() = _propertyInvalidationFlow
|
||||
|
||||
|
@ -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<MutableVisionGroup.StructureChange> = MutableSharedFlow(
|
||||
onBufferOverflow = BufferOverflow.DROP_OLDEST
|
||||
)
|
||||
private val _structureChanges: MutableSharedFlow<MutableVisionGroup.StructureChange> = MutableSharedFlow()
|
||||
|
||||
override val structureChanges: SharedFlow<MutableVisionGroup.StructureChange> get() = _structureChanges
|
||||
|
||||
|
@ -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,7 +128,7 @@ 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")
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -38,6 +38,7 @@ public class SolidGroup : VisionGroupBase(), Solid, PrototypeHolder {
|
||||
/**
|
||||
* Create or edit prototype node as a group
|
||||
*/
|
||||
@VisionBuilder
|
||||
public fun prototypes(builder: VisionContainerBuilder<Solid>.() -> Unit): Unit {
|
||||
(prototypes ?: Prototypes().also {
|
||||
prototypes = it
|
||||
@ -107,10 +108,9 @@ internal class Prototypes(
|
||||
children: Map<NameToken, Vision> = emptyMap(),
|
||||
) : VisionGroupBase(), PrototypeHolder {
|
||||
|
||||
override var parent: VisionGroup? = null
|
||||
|
||||
private val _children = HashMap(children)
|
||||
override val children: Map<NameToken, Vision> get() = _children
|
||||
init {
|
||||
childrenInternal.putAll(children)
|
||||
}
|
||||
|
||||
override val prototypes: MutableVisionGroup get() = this
|
||||
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -22,7 +22,7 @@ import kotlin.reflect.KClass
|
||||
public object ThreeCanvasLabelFactory : ThreeFactory<SolidLabel> {
|
||||
override val type: KClass<in SolidLabel> 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}"
|
||||
|
@ -25,7 +25,6 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
|
||||
|
||||
private val objectFactories = HashMap<KClass<out Solid>, 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<Solid>? = 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")
|
||||
}
|
||||
}
|
||||
|
@ -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<SolidReferenceGroup> {
|
||||
public object ThreeReferenceFactory : ThreeFactory<SolidReferenceGroup> {
|
||||
private val cache = HashMap<Solid, Object3D>()
|
||||
|
||||
override val type: KClass<SolidReferenceGroup> = 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)
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user