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
|
@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()) {
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
@ -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,
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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 }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user