Fixing new property update model

This commit is contained in:
Alexander Nozik 2020-12-16 13:24:38 +03:00
parent 929832f3a5
commit 6a6d9659ca
17 changed files with 55 additions and 50 deletions

View File

@ -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")

View File

@ -5,6 +5,7 @@ import hep.dataforge.names.plus
import hep.dataforge.names.startsWith
import hep.dataforge.values.asValue
import hep.dataforge.vision.getProperty
import hep.dataforge.vision.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"

View File

@ -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
)
}

View File

@ -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()) {

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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 = {

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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}"

View File

@ -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")
}
}

View File

@ -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)

View File

@ -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
}