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
private val listeners = HashSet<Listener>()
private val structureChangeListeners = HashSet<StructureChangeListeners>()
/**
* Add listener for children change
*/
override fun onChildrenChange(owner: Any?, action: (Name, VisualObject?) -> Unit) {
listeners.add(Listener(owner, action))
structureChangeListeners.add(StructureChangeListeners(owner, action))
}
/**
* Remove children change listener
*/
override fun removeChildrenChangeListener(owner: Any?) {
listeners.removeAll { it.owner === owner }
structureChangeListeners.removeAll { it.owner === owner }
}
// /**
// * Propagate children change event upwards
// */
// protected fun childrenChanged(name: Name, child: VisualObject?) {
//
// }
/**
* Propagate children change event upwards
*/
protected fun childrenChanged(name: Name, child: VisualObject?) {
structureChangeListeners.forEach { it.callback(name, child) }
}
/**
* Remove a child with given name token
@ -62,7 +62,7 @@ abstract class AbstractVisualGroup : AbstractVisualObject(), VisualGroup {
/**
* 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
@ -99,7 +99,7 @@ abstract class AbstractVisualGroup : AbstractVisualObject(), VisualGroup {
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()) {

View File

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

View File

@ -21,6 +21,11 @@ interface VisualObject : MetaRepr, Configurable {
@Transient
var parent: VisualObject?
/**
* A style which is set externally and could not be modified from inside
*/
var style: Meta?
/**
* 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
@ -8,15 +8,20 @@ import hep.dataforge.meta.Config
import hep.dataforge.meta.MetaBuilder
import hep.dataforge.meta.MetaItem
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.VisualGroup
import hep.dataforge.vis.common.VisualObject
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import kotlinx.serialization.UseSerializers
/**
* A proxy [VisualObject3D] to reuse a template object
*/
@Serializable
class Proxy(val templateName: Name) : AbstractVisualObject(), VisualObject3D {
class Proxy(val templateName: Name) : AbstractVisualObject(), VisualGroup, VisualObject3D {
override var position: 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
*/
val template: VisualObject3D
get() = (parent as? VisualGroup3D)?.getTemplate(templateName)
val template: VisualObject3D by lazy {
(parent as? VisualGroup3D)?.getTemplate(templateName)
?: error("Template with name $templateName not found in $parent")
}
override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? {
return if (inherit) {
@ -45,16 +50,58 @@ class Proxy(val templateName: Name) : AbstractVisualObject(), VisualObject3D {
//TODO add reference to child
updatePosition()
}
}
//fun VisualGroup3D.proxy(
// templateName: Name,
// //name: String? = null,
// builder: VisualGroup3D.() -> Unit
//): Proxy {
// val template = getTemplate(templateName) ?: templates.builder()
// return Proxy(this, templateName).also { set(name, it) }
//}
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))
}
override fun removeChildrenChangeListener(owner: Any?) {
listeners.removeAll { it.owner == owner }
}
override fun set(name: Name, child: VisualObject?) {
error("Content change not supported for proxy")
}
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(
templateName: Name,

View File

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

View File

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

View File

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

View File

@ -1,14 +1,23 @@
package hep.dataforge.vis.spatial.three
import hep.dataforge.vis.common.VisualObject
import hep.dataforge.vis.spatial.Proxy
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.objects.Mesh
class ThreeProxyFactory(val three: ThreePlugin) : ThreeFactory<Proxy> {
private val cache = HashMap<VisualObject3D, Object3D>()
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 {
val template = obj.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 = cachedObject.clone()
val object3D = cachedObject.clone()
object3D.updatePosition(obj)
mesh.updatePosition(obj)
return mesh
obj.onChildrenChange(object3D) { name, propertyHolder ->
(object3D.findChild(name) as? Mesh)?.updateProperties(propertyHolder)
}
return object3D
}
}