Properties for cached objects inside proxies (not tested)
This commit is contained in:
parent
39b70a6064
commit
dffc1e039b
@ -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()) {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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,
|
||||
|
@ -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) {
|
||||
_children.remove(token)
|
||||
override fun removeChild(token: NameToken) {
|
||||
_children.remove(token)
|
||||
childrenChanged(token.asName(), null)
|
||||
}
|
||||
|
||||
override fun setChild(token: NameToken, child: VisualObject) {
|
||||
if (child.parent == null) {
|
||||
child.parent = this
|
||||
} else {
|
||||
if (child.parent == null) {
|
||||
child.parent = this
|
||||
} else {
|
||||
error("Can't reassign existing parent for $child")
|
||||
}
|
||||
_children[token] = child
|
||||
error("Can't reassign existing parent for $child")
|
||||
}
|
||||
_children[token] = child
|
||||
childrenChanged(token.asName(), child)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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 }
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user