Encapsulate prototypes.

This commit is contained in:
Alexander Nozik 2020-12-19 17:44:24 +03:00
parent 6939fba292
commit ccb916cff7
6 changed files with 96 additions and 80 deletions

View File

@ -39,9 +39,10 @@ fun main() {
val randomI = Random.nextInt(1, 4) val randomI = Random.nextInt(1, 4)
val randomJ = Random.nextInt(1, 4) val randomJ = Random.nextInt(1, 4)
val target = "layer[$randomLayer].segment[$randomI,$randomJ]".toName() val target = "layer[$randomLayer].segment[$randomI,$randomJ]".toName()
(sat[target] as? Solid)?.color("red") val targetVision = sat[target] as Solid
targetVision.color("red")
delay(300) delay(300)
(sat[target] as? Solid)?.color("green") targetVision.color("green")
delay(10) delay(10)
} }
} }

View File

@ -11,8 +11,10 @@ import hep.dataforge.names.Name
import hep.dataforge.names.asName import hep.dataforge.names.asName
import hep.dataforge.values.ValueType import hep.dataforge.values.ValueType
import hep.dataforge.vision.Vision.Companion.STYLE_KEY import hep.dataforge.vision.Vision.Companion.STYLE_KEY
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.launch
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient import kotlinx.serialization.Transient
@ -68,7 +70,7 @@ public open class VisionBase : Vision {
yieldAll(getStyleItems(name)) yieldAll(getStyleItems(name))
} }
if (inherit ?: descriptor?.get(name)?.inherited == true) { if (inherit ?: descriptor?.get(name)?.inherited == true) {
yield(parent?.getProperty(name, inherit)) yield(parent?.getProperty(name, inherit, includeStyles, includeDefaults))
} }
yield(descriptor?.get(name)?.defaultItem()) yield(descriptor?.get(name)?.defaultItem())
}.merge() }.merge()
@ -101,8 +103,9 @@ public open class VisionBase : Vision {
if (propertyName == STYLE_KEY) { if (propertyName == STYLE_KEY) {
updateStyles(styles) updateStyles(styles)
} }
GlobalScope.launch {
_propertyInvalidationFlow.tryEmit(propertyName) _propertyInvalidationFlow.emit(propertyName)
}
} }
public fun configure(block: MutableMeta<*>.() -> Unit) { public fun configure(block: MutableMeta<*>.() -> Unit) {

View File

@ -1,8 +1,10 @@
package hep.dataforge.vision package hep.dataforge.vision
import hep.dataforge.names.* import hep.dataforge.names.*
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.launch
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient import kotlinx.serialization.Transient
@ -43,7 +45,9 @@ public open class VisionGroupBase : VisionBase(), MutableVisionGroup {
* Propagate children change event upwards * Propagate children change event upwards
*/ */
private fun childrenChanged(name: NameToken, before: Vision?, after: Vision?) { private fun childrenChanged(name: NameToken, before: Vision?, after: Vision?) {
_structureChanges.tryEmit(MutableVisionGroup.StructureChange(name, before, after)) GlobalScope.launch {
_structureChanges.emit(MutableVisionGroup.StructureChange(name, before, after))
}
} }
/** /**

View File

@ -16,7 +16,11 @@ import kotlinx.serialization.encoding.Encoder
public interface PrototypeHolder { public interface PrototypeHolder {
public val parent: VisionGroup? public val parent: VisionGroup?
public val prototypes: MutableVisionGroup?
@VisionBuilder
public fun prototypes(builder: VisionContainerBuilder<Solid>.() -> Unit)
public fun getPrototype(name: Name): Solid?
} }
/** /**
@ -28,18 +32,24 @@ public class SolidGroup : VisionGroupBase(), Solid, PrototypeHolder {
override val descriptor: NodeDescriptor get() = Solid.descriptor override val descriptor: NodeDescriptor get() = Solid.descriptor
/** /**
* A container for templates visible inside this group * A container for templates visible inside this group
*/ */
@Serializable(Prototypes.Companion::class) @Serializable(Prototypes.Companion::class)
override var prototypes: MutableVisionGroup? = null @SerialName("prototypes")
private set internal var prototypes: MutableVisionGroup? = null
/**
* Ger a prototype redirecting the request to the parent if prototype is not found
*/
override fun getPrototype(name: Name): Solid? =
prototypes?.get(name) as? Solid ?: (parent as? PrototypeHolder)?.getPrototype(name)
/** /**
* Create or edit prototype node as a group * Create or edit prototype node as a group
*/ */
@VisionBuilder override fun prototypes(builder: VisionContainerBuilder<Solid>.() -> Unit): Unit {
public fun prototypes(builder: VisionContainerBuilder<Solid>.() -> Unit): Unit {
(prototypes ?: Prototypes().also { (prototypes ?: Prototypes().also {
prototypes = it prototypes = it
it.parent = this it.parent = this
@ -81,12 +91,6 @@ public fun SolidGroup(block: SolidGroup.() -> Unit): SolidGroup {
return SolidGroup().apply(block) return SolidGroup().apply(block)
} }
/**
* Ger a prototype redirecting the request to the parent if prototype is not found
*/
public tailrec fun PrototypeHolder.getPrototype(name: Name): Solid? =
prototypes?.get(name) as? Solid ?: (parent as? PrototypeHolder)?.getPrototype(name)
@VisionBuilder @VisionBuilder
public fun VisionContainerBuilder<Vision>.group( public fun VisionContainerBuilder<Vision>.group(
name: Name = Name.EMPTY, name: Name = Name.EMPTY,
@ -106,14 +110,12 @@ public fun VisionContainerBuilder<Vision>.group(name: String, action: SolidGroup
@Serializable(Prototypes.Companion::class) @Serializable(Prototypes.Companion::class)
internal class Prototypes( internal class Prototypes(
children: Map<NameToken, Vision> = emptyMap(), children: Map<NameToken, Vision> = emptyMap(),
) : VisionGroupBase(), PrototypeHolder { ) : VisionGroupBase() {
init { init {
childrenInternal.putAll(children) childrenInternal.putAll(children)
} }
override val prototypes: MutableVisionGroup get() = this
override fun attachChildren() { override fun attachChildren() {
children.values.forEach { children.values.forEach {
it.parent = parent it.parent = parent
@ -131,7 +133,7 @@ internal class Prototypes(
): MetaItem<*>? = null ): MetaItem<*>? = null
override fun setProperty(name: Name, item: MetaItem<*>?, notify: Boolean) { override fun setProperty(name: Name, item: MetaItem<*>?, notify: Boolean) {
TODO("Not yet implemented") error("Can't ser property of prototypes container")
} }
override val descriptor: NodeDescriptor? = null override val descriptor: NodeDescriptor? = null

View File

@ -90,10 +90,20 @@ public class SolidMaterial : Scheme() {
//must be lazy to avoid initialization bug //must be lazy to avoid initialization bug
NodeDescriptor { NodeDescriptor {
value(COLOR_KEY) { value(COLOR_KEY) {
inherited = true
usesStyles = true
type(ValueType.STRING, ValueType.NUMBER) type(ValueType.STRING, ValueType.NUMBER)
widgetType = "color" widgetType = "color"
} }
// value(SPECULAR_COLOR_KEY) {
// inherited = true
// usesStyles = true
// type(ValueType.STRING, ValueType.NUMBER)
// widgetType = "color"
// }
value(OPACITY_KEY) { value(OPACITY_KEY) {
inherited = true
usesStyles = true
type(ValueType.NUMBER) type(ValueType.NUMBER)
default(1.0) default(1.0)
attributes { attributes {
@ -104,6 +114,8 @@ public class SolidMaterial : Scheme() {
widgetType = "slider" widgetType = "slider"
} }
value(WIREFRAME_KEY) { value(WIREFRAME_KEY) {
inherited = true
usesStyles = true
type(ValueType.BOOLEAN) type(ValueType.BOOLEAN)
default(false) default(false)
} }
@ -124,11 +136,7 @@ public var Solid.material: SolidMaterial?
@VisionBuilder @VisionBuilder
public fun Solid.material(builder: SolidMaterial.() -> Unit) { public fun Solid.material(builder: SolidMaterial.() -> Unit) {
val node = allProperties( val node = allProperties(inherit = true).getItem(MATERIAL_KEY).node
inherit = true,
includeStyles = true,
includeDefaults = true
).getItem(MATERIAL_KEY).node
if (node != null) { if (node != null) {
SolidMaterial.update(node, builder) SolidMaterial.update(node, builder)
} else { } else {

View File

@ -46,42 +46,41 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
as ThreeFactory<Solid>? as ThreeFactory<Solid>?
} }
public fun buildObject3D(obj: Solid): Object3D { public fun buildObject3D(obj: Solid): Object3D = when (obj) {
return when (obj) { is ThreeVision -> obj.render(this)
is ThreeVision -> obj.render(this) is SolidReferenceGroup -> ThreeReferenceFactory(this, obj)
is SolidReferenceGroup -> ThreeReferenceFactory(this, obj) is SolidGroup -> {
is SolidGroup -> { val group = ThreeGroup()
val group = ThreeGroup() obj.children.forEach { (token, child) ->
obj.children.forEach { (token, child) -> if (child is Solid && child.ignore != true) {
if (child is Solid && child.ignore != true) { try {
try { val object3D = buildObject3D(child)
val object3D = buildObject3D(child) group[token] = object3D
group[token] = object3D } catch (ex: Throwable) {
} catch (ex: Throwable) { logger.error(ex) { "Failed to render $child" }
logger.error(ex) { "Failed to render $child" } ex.printStackTrace()
ex.printStackTrace()
}
} }
} }
}
group.apply { group.apply {
updatePosition(obj) updatePosition(obj)
//obj.onChildrenChange() //obj.onChildrenChange()
obj.propertyNameFlow.onEach { name -> obj.propertyNameFlow.onEach { name ->
if ( if (
name.startsWith(Solid.POSITION_KEY) || name.startsWith(Solid.POSITION_KEY) ||
name.startsWith(Solid.ROTATION) || name.startsWith(Solid.ROTATION) ||
name.startsWith(Solid.SCALE_KEY) name.startsWith(Solid.SCALE_KEY)
) { ) {
//update position of mesh using this object //update position of mesh using this object
updatePosition(obj) updatePosition(obj)
} else if (name == Vision.VISIBLE_KEY) { } else if (name == Vision.VISIBLE_KEY) {
visible = obj.visible ?: true visible = obj.visible ?: true
} }
}.launchIn(updateScope) }.launchIn(updateScope)
obj.structureChanges.onEach { (nameToken, _, child) -> obj.structureChanges.onEach { (nameToken, _, child) ->
// if (name.isEmpty()) { // if (name.isEmpty()) {
// logger.error { "Children change with empty name on $group" } // logger.error { "Children change with empty name on $group" }
// return@onChildrenChange // return@onChildrenChange
@ -90,32 +89,31 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
// val parentName = name.cutLast() // val parentName = name.cutLast()
// val childName = name.last()!! // val childName = name.last()!!
//removing old object //removing old object
findChild(nameToken.asName())?.let { oldChild -> findChild(nameToken.asName())?.let { oldChild ->
oldChild.parent?.remove(oldChild) oldChild.parent?.remove(oldChild)
} }
//adding new object //adding new object
if (child != null && child is Solid) { if (child != null && child is Solid) {
try { try {
val object3D = buildObject3D(child) val object3D = buildObject3D(child)
set(nameToken, object3D) set(nameToken, object3D)
} catch (ex: Throwable) { } catch (ex: Throwable) {
logger.error(ex) { "Failed to render $child" } logger.error(ex) { "Failed to render $child" }
}
} }
}.launchIn(updateScope) }
} }.launchIn(updateScope)
} }
is Composite -> compositeFactory(this, obj) }
else -> { is Composite -> compositeFactory(this, obj)
//find specialized factory for this type if it is present else -> {
val factory: ThreeFactory<Solid>? = findObjectFactory(obj::class) //find specialized factory for this type if it is present
when { val factory: ThreeFactory<Solid>? = findObjectFactory(obj::class)
factory != null -> factory(this, obj) when {
obj is GeometrySolid -> ThreeShapeFactory(this, obj) factory != null -> factory(this, obj)
else -> error("Renderer for ${obj::class} not found") obj is GeometrySolid -> ThreeShapeFactory(this, obj)
} else -> error("Renderer for ${obj::class} not found")
} }
} }
} }