Properties for cached objects inside proxies (not tested)

This commit is contained in:
Alexander Nozik 2019-09-09 22:09:08 +03:00
parent 39b70a6064
commit dffc1e039b
8 changed files with 134 additions and 50 deletions

View File

@ -28,31 +28,31 @@ abstract class AbstractVisualGroup : AbstractVisualObject(), VisualGroup {
} }
} }
private data class Listener(val owner: Any?, val callback: (Name, VisualObject?) -> Unit) private data class StructureChangeListeners(val owner: Any?, val callback: (Name, VisualObject?) -> Unit)
@Transient @Transient
private val listeners = HashSet<Listener>() private val structureChangeListeners = HashSet<StructureChangeListeners>()
/** /**
* Add listener for children change * Add listener for children change
*/ */
override fun onChildrenChange(owner: Any?, action: (Name, VisualObject?) -> Unit) { override fun onChildrenChange(owner: Any?, action: (Name, VisualObject?) -> Unit) {
listeners.add(Listener(owner, action)) structureChangeListeners.add(StructureChangeListeners(owner, action))
} }
/** /**
* Remove children change listener * Remove children change listener
*/ */
override fun removeChildrenChangeListener(owner: Any?) { override fun removeChildrenChangeListener(owner: Any?) {
listeners.removeAll { it.owner === owner } structureChangeListeners.removeAll { it.owner === owner }
} }
// /** /**
// * Propagate children change event upwards * Propagate children change event upwards
// */ */
// protected fun childrenChanged(name: Name, child: VisualObject?) { protected fun childrenChanged(name: Name, child: VisualObject?) {
// structureChangeListeners.forEach { it.callback(name, child) }
// } }
/** /**
* Remove a child with given name token * Remove a child with given name token
@ -62,7 +62,7 @@ abstract class AbstractVisualGroup : AbstractVisualObject(), VisualGroup {
/** /**
* Add, remove or replace child with given name * Add, remove or replace child with given name
*/ */
protected abstract fun setChild(token: NameToken, child: VisualObject?) protected abstract fun setChild(token: NameToken, child: VisualObject)
/** /**
* Add a static child. Statics could not be found by name, removed or replaced * Add a static child. Statics could not be found by name, removed or replaced
@ -99,7 +99,7 @@ abstract class AbstractVisualGroup : AbstractVisualObject(), VisualGroup {
parent[name.last()!!.asName()] = child parent[name.last()!!.asName()] = child
} }
} }
listeners.forEach { it.callback(name, child) } structureChangeListeners.forEach { it.callback(name, child) }
} }
operator fun set(key: String, child: VisualObject?) = if (key.isBlank()) { operator fun set(key: String, child: VisualObject?) = if (key.isBlank()) {

View File

@ -2,6 +2,7 @@ package hep.dataforge.vis.common
import hep.dataforge.meta.* import hep.dataforge.meta.*
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.asName
import kotlinx.serialization.Transient import kotlinx.serialization.Transient
internal data class PropertyListener( internal data class PropertyListener(
@ -14,6 +15,19 @@ abstract class AbstractVisualObject : VisualObject {
@Transient @Transient
override var parent: VisualObject? = null override var parent: VisualObject? = null
override var style: Meta? = null
set(value) {
//notify about style removed
style?.items?.forEach {(name, value) ->
propertyChanged(name.asName(), value, null)
}
field = value
//notify about style adition
value?.items?.forEach { (name, value) ->
propertyChanged(name.asName(), null, value)
}
}
@Transient @Transient
private val listeners = HashSet<PropertyListener>() private val listeners = HashSet<PropertyListener>()
@ -31,7 +45,8 @@ abstract class AbstractVisualObject : VisualObject {
listeners.removeAll { it.owner == owner } listeners.removeAll { it.owner == owner }
} }
abstract var properties: Config? protected abstract var properties: Config?
override val config: Config override val config: Config
get() = properties ?: Config().also { config -> get() = properties ?: Config().also { config ->
properties = config.apply { onChange(this, ::propertyChanged) } properties = config.apply { onChange(this, ::propertyChanged) }
@ -43,9 +58,9 @@ abstract class AbstractVisualObject : VisualObject {
override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? { override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? {
return if (inherit) { return if (inherit) {
properties?.get(name) ?: parent?.getProperty(name, inherit) style?.get(name) ?: properties?.get(name) ?: parent?.getProperty(name, inherit)
} else { } else {
properties?.get(name) style?.get(name) ?: properties?.get(name)
} }
} }

View File

@ -21,6 +21,11 @@ interface VisualObject : MetaRepr, Configurable {
@Transient @Transient
var parent: VisualObject? var parent: VisualObject?
/**
* A style which is set externally and could not be modified from inside
*/
var style: Meta?
/** /**
* Set property for this object * Set property for this object
*/ */

View File

@ -1,4 +1,4 @@
@file:UseSerializers(Point3DSerializer::class, NameSerializer::class) @file:UseSerializers(Point3DSerializer::class, NameSerializer::class, ConfigSerializer::class)
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
@ -8,15 +8,20 @@ import hep.dataforge.meta.Config
import hep.dataforge.meta.MetaBuilder import hep.dataforge.meta.MetaBuilder
import hep.dataforge.meta.MetaItem import hep.dataforge.meta.MetaItem
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.NameToken
import hep.dataforge.names.asName
import hep.dataforge.vis.common.AbstractVisualObject import hep.dataforge.vis.common.AbstractVisualObject
import hep.dataforge.vis.common.VisualGroup
import hep.dataforge.vis.common.VisualObject
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import kotlinx.serialization.UseSerializers import kotlinx.serialization.UseSerializers
/** /**
* A proxy [VisualObject3D] to reuse a template object * A proxy [VisualObject3D] to reuse a template object
*/ */
@Serializable @Serializable
class Proxy(val templateName: Name) : AbstractVisualObject(), VisualObject3D { class Proxy(val templateName: Name) : AbstractVisualObject(), VisualGroup, VisualObject3D {
override var position: Point3D? = null override var position: Point3D? = null
override var rotation: Point3D? = null override var rotation: Point3D? = null
@ -28,10 +33,10 @@ class Proxy(val templateName: Name) : AbstractVisualObject(), VisualObject3D {
/** /**
* Recursively search for defined template in the parent * Recursively search for defined template in the parent
*/ */
val template: VisualObject3D val template: VisualObject3D by lazy {
get() = (parent as? VisualGroup3D)?.getTemplate(templateName) (parent as? VisualGroup3D)?.getTemplate(templateName)
?: error("Template with name $templateName not found in $parent") ?: error("Template with name $templateName not found in $parent")
}
override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? { override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? {
return if (inherit) { return if (inherit) {
@ -45,16 +50,58 @@ class Proxy(val templateName: Name) : AbstractVisualObject(), VisualObject3D {
//TODO add reference to child //TODO add reference to child
updatePosition() updatePosition()
} }
override val children: Map<NameToken, ProxyChild>
get() = (template as? VisualGroup)?.children?.mapValues {
ProxyChild(it.key.asName())
} ?: emptyMap()
private data class ProxyChangeListeners(val owner: Any?, val callback: (Name, VisualObject?) -> Unit)
@Transient
private val listeners = HashSet<ProxyChangeListeners>()
override fun onChildrenChange(owner: Any?, action: (Name, VisualObject?) -> Unit) {
listeners.add(ProxyChangeListeners(owner, action))
} }
//fun VisualGroup3D.proxy( override fun removeChildrenChangeListener(owner: Any?) {
// templateName: Name, listeners.removeAll { it.owner == owner }
// //name: String? = null, }
// builder: VisualGroup3D.() -> Unit
//): Proxy { override fun set(name: Name, child: VisualObject?) {
// val template = getTemplate(templateName) ?: templates.builder() error("Content change not supported for proxy")
// return Proxy(this, templateName).also { set(name, it) } }
//}
private val propertyCache: HashMap<Name, Config> = HashMap()
private fun Config.attachListener(obj: VisualObject) {
onChange(this@Proxy) { name, before, after ->
listeners.forEach { listener ->
listener.callback(name, obj)
}
}
}
inner class ProxyChild(val name: Name) : AbstractVisualObject() {
override var properties: Config?
get() = propertyCache.getOrPut(name) {
Config().apply {
attachListener(this@ProxyChild)
}
}
set(value) {
if (value == null) {
propertyCache.remove(name)?.removeListener(this@Proxy)
} else {
propertyCache[name] = value.apply {
attachListener(this@ProxyChild)
}
}
}
}
}
inline fun VisualGroup3D.ref( inline fun VisualGroup3D.ref(
templateName: Name, templateName: Name,

View File

@ -1,9 +1,9 @@
@file:UseSerializers(Point3DSerializer::class, ConfigSerializer::class, NameTokenSerializer::class) @file:UseSerializers(Point3DSerializer::class, ConfigSerializer::class, NameTokenSerializer::class)
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.io.ConfigSerializer import hep.dataforge.io.ConfigSerializer
import hep.dataforge.meta.Config import hep.dataforge.meta.Config
import hep.dataforge.meta.Configurable
import hep.dataforge.meta.MetaBuilder import hep.dataforge.meta.MetaBuilder
import hep.dataforge.meta.set import hep.dataforge.meta.set
import hep.dataforge.names.Name import hep.dataforge.names.Name
@ -16,7 +16,7 @@ import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers import kotlinx.serialization.UseSerializers
@Serializable @Serializable
class VisualGroup3D : AbstractVisualGroup(), VisualObject3D, Configurable { class VisualGroup3D() : AbstractVisualGroup(), VisualObject3D {
/** /**
* A container for templates visible inside this group * A container for templates visible inside this group
*/ */
@ -36,21 +36,26 @@ class VisualGroup3D : AbstractVisualGroup(), VisualObject3D, Configurable {
private val _children = HashMap<NameToken, VisualObject>() private val _children = HashMap<NameToken, VisualObject>()
override val children: Map<NameToken, VisualObject> get() = _children override val children: Map<NameToken, VisualObject> get() = _children
override fun removeChild(token: NameToken) { init {
_children.remove(token) //Do after deserialization
_children.values.forEach {
it.parent = this
}
} }
override fun setChild(token: NameToken, child: VisualObject?) { override fun removeChild(token: NameToken) {
if (child == null) {
_children.remove(token) _children.remove(token)
} else { childrenChanged(token.asName(), null)
}
override fun setChild(token: NameToken, child: VisualObject) {
if (child.parent == null) { if (child.parent == null) {
child.parent = this child.parent = this
} else { } else {
error("Can't reassign existing parent for $child") error("Can't reassign existing parent for $child")
} }
_children[token] = child _children[token] = child
} childrenChanged(token.asName(), child)
} }
/** /**

View File

@ -90,15 +90,15 @@ var VisualObject3D.detail: Int?
get() = getProperty(DETAIL_KEY, false).int get() = getProperty(DETAIL_KEY, false).int
set(value) = setProperty(DETAIL_KEY, value) set(value) = setProperty(DETAIL_KEY, value)
var VisualObject3D.material: Meta? var VisualObject.material: Meta?
get() = getProperty(MATERIAL_KEY).node get() = getProperty(MATERIAL_KEY).node
set(value) = setProperty(MATERIAL_KEY, value) set(value) = setProperty(MATERIAL_KEY, value)
var VisualObject3D.visible: Boolean? var VisualObject.visible: Boolean?
get() = getProperty(VISIBLE_KEY).boolean get() = getProperty(VISIBLE_KEY).boolean
set(value) = setProperty(VISIBLE_KEY, value) set(value) = setProperty(VISIBLE_KEY, value)
fun VisualObject3D.color(rgb: Int) { fun VisualObject.color(rgb: Int) {
material = buildMeta { "color" to rgb } material = buildMeta { "color" to rgb }
} }

View File

@ -40,6 +40,7 @@ class ThreePlugin : AbstractPlugin() {
fun buildObject3D(obj: VisualObject3D): Object3D { fun buildObject3D(obj: VisualObject3D): Object3D {
return when (obj) { return when (obj) {
is Proxy -> proxyFactory(obj)
is VisualGroup3D -> { is VisualGroup3D -> {
val group = info.laht.threekt.objects.Group() val group = info.laht.threekt.objects.Group()
obj.children.forEach { (name, child) -> obj.children.forEach { (name, child) ->
@ -49,7 +50,6 @@ class ThreePlugin : AbstractPlugin() {
object3D.name = name.toString() object3D.name = name.toString()
group.add(object3D) group.add(object3D)
} catch (ex: Throwable) { } catch (ex: Throwable) {
// console.error(ex)
logger.error(ex) { "Failed to render $name" } logger.error(ex) { "Failed to render $name" }
} }
} }
@ -61,7 +61,6 @@ class ThreePlugin : AbstractPlugin() {
} }
} }
is Composite -> compositeFactory(obj) is Composite -> compositeFactory(obj)
is Proxy -> proxyFactory(obj)
else -> { else -> {
//find specialized factory for this type if it is present //find specialized factory for this type if it is present
val factory = findObjectFactory(obj::class) val factory = findObjectFactory(obj::class)

View File

@ -1,14 +1,23 @@
package hep.dataforge.vis.spatial.three package hep.dataforge.vis.spatial.three
import hep.dataforge.vis.common.VisualObject
import hep.dataforge.vis.spatial.Proxy import hep.dataforge.vis.spatial.Proxy
import hep.dataforge.vis.spatial.VisualObject3D import hep.dataforge.vis.spatial.VisualObject3D
import hep.dataforge.vis.spatial.material
import hep.dataforge.vis.spatial.visible
import info.laht.threekt.core.Object3D import info.laht.threekt.core.Object3D
import info.laht.threekt.objects.Mesh
class ThreeProxyFactory(val three: ThreePlugin) : ThreeFactory<Proxy> { class ThreeProxyFactory(val three: ThreePlugin) : ThreeFactory<Proxy> {
private val cache = HashMap<VisualObject3D, Object3D>() private val cache = HashMap<VisualObject3D, Object3D>()
override val type = Proxy::class override val type = Proxy::class
private fun Mesh.updateProperties(obj: VisualObject?) {
material = obj?.material.jsMaterial()
visible = obj?.visible ?: true
}
override fun invoke(obj: Proxy): Object3D { override fun invoke(obj: Proxy): Object3D {
val template = obj.template val template = obj.template
val cachedObject = cache.getOrPut(template) { val cachedObject = cache.getOrPut(template) {
@ -16,9 +25,13 @@ class ThreeProxyFactory(val three: ThreePlugin) : ThreeFactory<Proxy> {
} }
//val mesh = Mesh(templateMesh.geometry as BufferGeometry, templateMesh.material) //val mesh = Mesh(templateMesh.geometry as BufferGeometry, templateMesh.material)
val mesh = cachedObject.clone() val object3D = cachedObject.clone()
object3D.updatePosition(obj)
mesh.updatePosition(obj) obj.onChildrenChange(object3D) { name, propertyHolder ->
return mesh (object3D.findChild(name) as? Mesh)?.updateProperties(propertyHolder)
}
return object3D
} }
} }