Add property flows

This commit is contained in:
Alexander Nozik 2022-08-13 12:45:10 +03:00
parent 0ea1ee056a
commit ecf4a6a198
No known key found for this signature in database
GPG Key ID: F7FCF2DD25C71357
53 changed files with 356 additions and 129 deletions

View File

@ -352,14 +352,14 @@ private fun SolidGroup.addRootVolume(
} }
block() block()
} }
set(combinedName?.let { Name.parse(it) }, group) setChild(combinedName?.let { Name.parse(it) }, group)
} else { } else {
val templateName = volumesName + volume.name val templateName = volumesName + volume.name
val existing = getPrototype(templateName) val existing = getPrototype(templateName)
if (existing == null) { if (existing == null) {
context.prototypeHolder.prototypes { context.prototypeHolder.prototypes {
val group = buildVolume(volume, context) val group = buildVolume(volume, context)
set(templateName, group) setChild(templateName, group)
} }
} }

View File

@ -5,7 +5,7 @@ import space.kscience.dataforge.meta.string
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.gdml.GdmlShowCase import space.kscience.gdml.GdmlShowCase
import space.kscience.visionforge.Vision import space.kscience.visionforge.Vision
import space.kscience.visionforge.get import space.kscience.visionforge.getChild
import space.kscience.visionforge.solid.Solid import space.kscience.visionforge.solid.Solid
import space.kscience.visionforge.solid.SolidMaterial import space.kscience.visionforge.solid.SolidMaterial
import space.kscience.visionforge.solid.material import space.kscience.visionforge.solid.material
@ -18,7 +18,7 @@ class GDMLVisionTest {
@Test @Test
fun testCubesStyles(){ fun testCubesStyles(){
val segment = cubes.children["composite-000.segment-0"] as Solid val segment = cubes.children.getChild("composite-000.segment-0") as Solid
println(segment.properties.getValue(Vision.STYLE_KEY)) println(segment.properties.getValue(Vision.STYLE_KEY))
// println(segment.computePropertyNode(SolidMaterial.MATERIAL_KEY)) // println(segment.computePropertyNode(SolidMaterial.MATERIAL_KEY))
// println(segment.computeProperty(SolidMaterial.MATERIAL_COLOR_KEY)) // println(segment.computeProperty(SolidMaterial.MATERIAL_COLOR_KEY))

View File

@ -48,6 +48,7 @@ kotlin {
implementation("io.ktor:ktor-server-cio:${ktorVersion}") implementation("io.ktor:ktor-server-cio:${ktorVersion}")
implementation("io.ktor:ktor-server-content-negotiation:${ktorVersion}") implementation("io.ktor:ktor-server-content-negotiation:${ktorVersion}")
implementation("io.ktor:ktor-serialization-kotlinx-json:${ktorVersion}") implementation("io.ktor:ktor-serialization-kotlinx-json:${ktorVersion}")
implementation("ch.qos.logback:logback-classic:1.2.11")
} }
} }
jsMain { jsMain {

View File

@ -3,7 +3,10 @@ package space.kscience.visionforge.solid.demo
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.visionforge.Vision import space.kscience.visionforge.Vision
import space.kscience.visionforge.solid.Solids
public interface VisionLayout<in V: Vision> { public interface VisionLayout<in V: Vision> {
val solids: Solids
public fun render(name: Name, vision: V, meta: Meta = Meta.EMPTY) public fun render(name: Name, vision: V, meta: Meta = Meta.EMPTY)
} }

View File

@ -5,6 +5,7 @@ import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.invoke import space.kscience.dataforge.meta.invoke
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.visionforge.Colors import space.kscience.visionforge.Colors
import space.kscience.visionforge.setAsRoot
import space.kscience.visionforge.solid.* import space.kscience.visionforge.solid.*
import space.kscience.visionforge.solid.specifications.Canvas3DOptions import space.kscience.visionforge.solid.specifications.Canvas3DOptions
import space.kscience.visionforge.visible import space.kscience.visionforge.visible
@ -22,6 +23,7 @@ fun VisionLayout<Solid>.demo(name: String, title: String = name, block: SolidGro
ambientLight{ ambientLight{
color.set(Colors.white) color.set(Colors.white)
} }
setAsRoot(solids.visionManager)
} }
render(Name.parse(name), vision, meta) render(Name.parse(name), vision, meta)
} }

View File

@ -16,6 +16,7 @@ import space.kscience.dataforge.meta.get
import space.kscience.dataforge.meta.string import space.kscience.dataforge.meta.string
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.visionforge.solid.Solid import space.kscience.visionforge.solid.Solid
import space.kscience.visionforge.solid.Solids
import space.kscience.visionforge.solid.three.ThreeCanvas import space.kscience.visionforge.solid.three.ThreeCanvas
import space.kscience.visionforge.solid.three.ThreePlugin import space.kscience.visionforge.solid.three.ThreePlugin
@ -27,6 +28,8 @@ class ThreeDemoGrid(element: Element) : VisionLayout<Solid> {
private val three = Global.fetch(ThreePlugin) private val three = Global.fetch(ThreePlugin)
override val solids: Solids get() = three.solids
init { init {
element.clear() element.clear()
element.append { element.append {

View File

@ -9,7 +9,7 @@ import space.kscience.dataforge.meta.number
import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.asName
import space.kscience.dataforge.names.startsWith import space.kscience.dataforge.names.startsWith
import space.kscience.visionforge.onPropertyChange import space.kscience.visionforge.onPropertyChange
import space.kscience.visionforge.set import space.kscience.visionforge.setChild
import space.kscience.visionforge.solid.SolidGroup import space.kscience.visionforge.solid.SolidGroup
import space.kscience.visionforge.solid.layer import space.kscience.visionforge.solid.layer
import space.kscience.visionforge.solid.three.* import space.kscience.visionforge.solid.three.*
@ -20,7 +20,7 @@ internal fun SolidGroup.varBox(
ySize: Number, ySize: Number,
name: String = "", name: String = "",
action: VariableBox.() -> Unit = {}, action: VariableBox.() -> Unit = {},
): VariableBox = VariableBox(xSize, ySize).apply(action).also { set(name, it) } ): VariableBox = VariableBox(xSize, ySize).apply(action).also { setChild(name, it) }
internal class VariableBox(val xSize: Number, val ySize: Number) : ThreeJsVision() { internal class VariableBox(val xSize: Number, val ySize: Number) : ThreeJsVision() {
@ -59,7 +59,7 @@ internal class VariableBox(val xSize: Number, val ySize: Number) : ThreeJsVision
material.color.setRGB(r.toFloat() / 256, g.toFloat() / 256, b.toFloat() / 256) material.color.setRGB(r.toFloat() / 256, g.toFloat() / 256, b.toFloat() / 256)
mesh.updateMatrix() mesh.updateMatrix()
} }
name.startsWith(MeshThreeFactory.EDGES_KEY) -> mesh.applyEdges(this@VariableBox) name.startsWith(ThreeMeshFactory.EDGES_KEY) -> mesh.applyEdges(this@VariableBox)
else -> mesh.updateProperty(this@VariableBox, name) else -> mesh.updateProperty(this@VariableBox, name)
} }
} }

View File

@ -10,9 +10,11 @@ import space.kscience.dataforge.names.Name
import space.kscience.visionforge.solid.FX3DPlugin import space.kscience.visionforge.solid.FX3DPlugin
import space.kscience.visionforge.solid.FXCanvas3D import space.kscience.visionforge.solid.FXCanvas3D
import space.kscience.visionforge.solid.Solid import space.kscience.visionforge.solid.Solid
import space.kscience.visionforge.solid.Solids
import tornadofx.* import tornadofx.*
class FXDemoGrid : View(title = "DataForge-vis FX demo"), VisionLayout<Solid> { class FXDemoGrid : View(title = "DataForge-vis FX demo"), VisionLayout<Solid> {
private val outputs = FXCollections.observableHashMap<Name, FXCanvas3D>() private val outputs = FXCollections.observableHashMap<Name, FXCanvas3D>()
override val root: Parent = borderpane { override val root: Parent = borderpane {
@ -24,6 +26,9 @@ class FXDemoGrid : View(title = "DataForge-vis FX demo"), VisionLayout<Solid> {
} }
private val fx3d = Global.fetch(FX3DPlugin) private val fx3d = Global.fetch(FX3DPlugin)
override val solids: Solids get() = fx3d.solids
override fun render(name: Name, vision: Solid, meta: Meta) { override fun render(name: Name, vision: Solid, meta: Meta) {
outputs.getOrPut(name) { FXCanvas3D(fx3d, canvasOptions) }.render(vision) outputs.getOrPut(name) { FXCanvas3D(fx3d, canvasOptions) }.render(vision)

View File

@ -13,6 +13,11 @@ kotlin {
api("org.jetbrains.kotlin-wrappers:kotlin-css") api("org.jetbrains.kotlin-wrappers:kotlin-css")
} }
} }
commonTest{
dependencies{
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:${space.kscience.gradle.KScienceVersions.coroutinesVersion}")
}
}
jsMain { jsMain {
dependencies { dependencies {
api("org.jetbrains.kotlin-wrappers:kotlin-extensions") api("org.jetbrains.kotlin-wrappers:kotlin-extensions")

View File

@ -11,10 +11,8 @@ import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.misc.Type import space.kscience.dataforge.misc.Type
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.asName
import space.kscience.dataforge.names.startsWith
import space.kscience.visionforge.AbstractVisionGroup.Companion.updateProperties import space.kscience.visionforge.AbstractVisionGroup.Companion.updateProperties
import space.kscience.visionforge.Vision.Companion.TYPE import space.kscience.visionforge.Vision.Companion.TYPE
import kotlin.reflect.KProperty1
/** /**
* A root type for display hierarchy * A root type for display hierarchy
@ -72,19 +70,4 @@ public fun Vision.onPropertyChange(
callback: (Name) -> Unit callback: (Name) -> Unit
): Job = properties.changes.onEach { ): Job = properties.changes.onEach {
callback(it) callback(it)
}.launchIn(scope ?: error("Orphan Vision can't observe properties")) }.launchIn(scope ?: error("Orphan Vision can't observe properties"))
public fun <V : Vision, T> V.useProperty(
property: KProperty1<V, T>,
scope: CoroutineScope? = manager?.context,
callBack: V.(T) -> Unit,
): Job {
//Pass initial value.
callBack(property.get(this))
return properties.changes.onEach { name ->
if (name.startsWith(property.name.asName())) {
callBack(property.get(this@useProperty))
}
}.launchIn(scope ?: error("Orphan Vision can't observe properties"))
}

View File

@ -47,7 +47,7 @@ public class VisionChangeBuilder(private val manager: VisionManager) : MutableVi
} }
} }
override fun set(name: Name?, child: Vision?) { override fun setChild(name: Name?, child: Vision?) {
if (name == null) error("Static children are not allowed in VisionChange") if (name == null) error("Static children are not allowed in VisionChange")
getOrPutChild(name).apply { getOrPutChild(name).apply {
vision = child vision = child
@ -109,7 +109,7 @@ private fun CoroutineScope.collectChange(
if (after != null) { if (after != null) {
collectChange(fullName, after, collector) collectChange(fullName, after, collector)
} }
collector()[fullName] = after collector().setChild(fullName, after)
}?.launchIn(this) }?.launchIn(this)
} }

View File

@ -11,12 +11,12 @@ import kotlin.jvm.Synchronized
public annotation class VisionBuilder public annotation class VisionBuilder
public interface VisionContainer<out V : Vision> { public interface VisionContainer<out V : Vision> {
public operator fun get(name: Name): V? public fun getChild(name: Name): V?
} }
public interface MutableVisionContainer<in V : Vision> { public interface MutableVisionContainer<in V : Vision> {
//TODO add documentation //TODO add documentation
public operator fun set(name: Name?, child: V?) public fun setChild(name: Name?, child: V?)
} }
/** /**
@ -33,10 +33,10 @@ public interface VisionChildren : VisionContainer<Vision> {
public operator fun get(token: NameToken): Vision? public operator fun get(token: NameToken): Vision?
override fun get(name: Name): Vision? = when (name.length) { override fun getChild(name: Name): Vision? = when (name.length) {
0 -> group 0 -> group
1 -> get(name.first()) 1 -> get(name.first())
else -> get(name.first())?.children?.get(name.cutFirst()) else -> get(name.first())?.children?.getChild(name.cutFirst())
} }
public companion object { public companion object {
@ -49,6 +49,10 @@ public interface VisionChildren : VisionContainer<Vision> {
} }
} }
public operator fun VisionChildren.get(name: Name): Vision? = getChild(name)
public operator fun VisionChildren.get(name: String): Vision? = getChild(name)
public fun VisionChildren.isEmpty(): Boolean = keys.isEmpty() public fun VisionChildren.isEmpty(): Boolean = keys.isEmpty()
public inline fun VisionChildren.forEach(block: (NameToken, Vision) -> Unit) { public inline fun VisionChildren.forEach(block: (NameToken, Vision) -> Unit) {
@ -61,7 +65,7 @@ public interface MutableVisionChildren : VisionChildren, MutableVisionContainer<
public operator fun set(token: NameToken, value: Vision?) public operator fun set(token: NameToken, value: Vision?)
override fun set(name: Name?, child: Vision?) { override fun setChild(name: Name?, child: Vision?) {
when { when {
name == null -> { name == null -> {
if (child != null) { if (child != null) {
@ -81,7 +85,7 @@ public interface MutableVisionChildren : VisionChildren, MutableVisionContainer<
val parent: MutableVisionGroup = currentParent as? MutableVisionGroup ?: group.createGroup().also { val parent: MutableVisionGroup = currentParent as? MutableVisionGroup ?: group.createGroup().also {
set(name.first(), it) set(name.first(), it)
} }
parent.children[name.cutFirst()] = child parent.children.setChild(name.cutFirst(), child)
} }
} }
} }
@ -89,6 +93,15 @@ public interface MutableVisionChildren : VisionChildren, MutableVisionContainer<
public fun clear() public fun clear()
} }
public operator fun MutableVisionChildren.set(name: Name?, vision: Vision?) {
setChild(name, vision)
}
public operator fun MutableVisionChildren.set(name: String?, vision: Vision?) {
setChild(name, vision)
}
/** /**
* Add a static child. Statics could not be found by name, removed or replaced. Changing statics also do not trigger events. * Add a static child. Statics could not be found by name, removed or replaced. Changing statics also do not trigger events.
*/ */
@ -102,11 +115,11 @@ public fun VisionChildren.asSequence(): Sequence<Pair<NameToken, Vision>> = sequ
public operator fun VisionChildren.iterator(): Iterator<Pair<NameToken, Vision>> = asSequence().iterator() public operator fun VisionChildren.iterator(): Iterator<Pair<NameToken, Vision>> = asSequence().iterator()
public operator fun <V : Vision> VisionContainer<V>.get(str: String): V? = get(Name.parse(str)) public fun <V : Vision> VisionContainer<V>.getChild(str: String): V? = getChild(Name.parse(str))
public operator fun <V : Vision> MutableVisionContainer<V>.set( public fun <V : Vision> MutableVisionContainer<V>.setChild(
str: String?, vision: V?, str: String?, vision: V?,
): Unit = set(str?.parseAsName(), vision) ): Unit = setChild(str?.parseAsName(), vision)
internal abstract class VisionChildrenImpl( internal abstract class VisionChildrenImpl(
override val group: MutableVisionGroup, override val group: MutableVisionGroup,

View File

@ -10,7 +10,6 @@ import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.NameToken import space.kscience.dataforge.names.NameToken
import space.kscience.dataforge.names.plus import space.kscience.dataforge.names.plus
import space.kscience.visionforge.Vision.Companion.STYLE_KEY import space.kscience.visionforge.Vision.Companion.STYLE_KEY
import kotlin.js.JsName
public interface VisionGroup : Vision { public interface VisionGroup : Vision {
@ -35,9 +34,9 @@ public abstract class AbstractVisionGroup : AbstractVision(), MutableVisionGroup
override fun update(change: VisionChange) { override fun update(change: VisionChange) {
change.children?.forEach { (name, change) -> change.children?.forEach { (name, change) ->
when { when {
change.delete -> children[name] = null change.delete -> children.setChild(name, null)
change.vision != null -> children[name] = change.vision change.vision != null -> children.setChild(name, change.vision)
else -> children[name]?.update(change) else -> children.getChild(name)?.update(change)
} }
} }
change.properties?.let { change.properties?.let {
@ -87,12 +86,28 @@ public abstract class AbstractVisionGroup : AbstractVision(), MutableVisionGroup
*/ */
@Serializable @Serializable
@SerialName("vision.group") @SerialName("vision.group")
public class SimpleVisionGroup : AbstractVisionGroup() { public class SimpleVisionGroup : AbstractVisionGroup(), MutableVisionContainer<Vision> {
override fun createGroup(): SimpleVisionGroup = SimpleVisionGroup() override fun createGroup(): SimpleVisionGroup = SimpleVisionGroup()
override fun setChild(name: Name?, child: Vision?) {
children.setChild(name, child)
}
} }
@JsName("createVisionGroup") @VisionBuilder
public fun VisionGroup(): VisionGroup = SimpleVisionGroup() public fun MutableVisionContainer<Vision>.group(
name: Name? = null,
builder: SimpleVisionGroup.() -> Unit = {},
): SimpleVisionGroup = SimpleVisionGroup().apply(builder).also { setChild(name, it) }
/**
* Define a group with given [name], attach it to this parent and return it.
*/
@VisionBuilder
public fun MutableVisionContainer<Vision>.group(
name: String,
builder: SimpleVisionGroup.() -> Unit = {},
): SimpleVisionGroup = SimpleVisionGroup().apply(builder).also { setChild(name, it) }
//fun VisualObject.findStyle(styleName: Name): Meta? { //fun VisualObject.findStyle(styleName: Name): Meta? {
// if (this is VisualGroup) { // if (this is VisualGroup) {

View File

@ -19,7 +19,7 @@ import space.kscience.visionforge.html.VisionOfNumberField
import space.kscience.visionforge.html.VisionOfTextField import space.kscience.visionforge.html.VisionOfTextField
import kotlin.reflect.KClass import kotlin.reflect.KClass
public class VisionManager(meta: Meta) : AbstractPlugin(meta) { public class VisionManager(meta: Meta) : AbstractPlugin(meta), MutableVisionContainer<Vision> {
override val tag: PluginTag get() = Companion.tag override val tag: PluginTag get() = Companion.tag
/** /**
@ -58,6 +58,10 @@ public class VisionManager(meta: Meta) : AbstractPlugin(meta) {
public fun encodeToMeta(vision: Vision, descriptor: MetaDescriptor? = null): Meta = public fun encodeToMeta(vision: Vision, descriptor: MetaDescriptor? = null): Meta =
encodeToJsonElement(vision).toMeta(descriptor) encodeToJsonElement(vision).toMeta(descriptor)
override fun setChild(name: Name?, child: Vision?) {
child?.setAsRoot(this)
}
public companion object : PluginFactory<VisionManager> { public companion object : PluginFactory<VisionManager> {
override val tag: PluginTag = PluginTag(name = "vision", group = PluginTag.DATAFORGE_GROUP) override val tag: PluginTag = PluginTag(name = "vision", group = PluginTag.DATAFORGE_GROUP)
override val type: KClass<out VisionManager> = VisionManager::class override val type: KClass<out VisionManager> = VisionManager::class

View File

@ -73,6 +73,19 @@ public interface MutableVisionProperties : VisionProperties {
) )
} }
public fun MutableVisionProperties.remove(name: Name){
setProperty(name, null)
}
public fun MutableVisionProperties.remove(name: String){
remove(name.parseAsName())
}
@VisionBuilder
public operator fun MutableVisionProperties.invoke(block: MutableMeta.() -> Unit) {
root(inherit = false, includeStyles = false).apply(block)
}
private class VisionPropertiesItem( private class VisionPropertiesItem(
val properties: MutableVisionProperties, val properties: MutableVisionProperties,
val nodeName: Name, val nodeName: Name,
@ -190,7 +203,7 @@ public abstract class AbstractVisionProperties(
} }
@Transient @Transient
private val _changes = MutableSharedFlow<Name>(10) private val _changes = MutableSharedFlow<Name>()
override val changes: SharedFlow<Name> get() = _changes override val changes: SharedFlow<Name> get() = _changes
@OptIn(DelicateCoroutinesApi::class) @OptIn(DelicateCoroutinesApi::class)

View File

@ -0,0 +1,55 @@
package space.kscience.visionforge
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.Value
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.parseAsName
import space.kscience.dataforge.names.startsWith
/**
* Create a flow of a specific property
*/
public fun Vision.flowProperty(
propertyName: Name,
inherit: Boolean? = null,
includeStyles: Boolean? = null,
): Flow<Meta> = flow {
//Pass initial value.
emit(properties.getProperty(propertyName, inherit, includeStyles))
properties.changes.collect { name ->
if (name.startsWith(propertyName)) {
emit(properties.getProperty(propertyName, inherit, includeStyles))
}
}
}
public fun Vision.flowProperty(
propertyName: String,
inherit: Boolean? = null,
includeStyles: Boolean? = null,
): Flow<Meta> = flowProperty(propertyName.parseAsName(), inherit, includeStyles)
/**
* Flow the value of specific property
*/
public fun Vision.flowPropertyValue(
propertyName: Name,
inherit: Boolean? = null,
includeStyles: Boolean? = null,
): Flow<Value?> = flow {
//Pass initial value.
emit(properties.getValue(propertyName, inherit, includeStyles))
properties.changes.collect { name ->
if (name.startsWith(propertyName)) {
emit(properties.getValue(propertyName, inherit, includeStyles))
}
}
}
public fun Vision.flowPropertyValue(
propertyName: String,
inherit: Boolean? = null,
includeStyles: Boolean? = null,
): Flow<Value?> = flowPropertyValue(propertyName.parseAsName(), inherit, includeStyles)

View File

@ -0,0 +1,54 @@
package space.kscience.visionforge
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.asName
import space.kscience.dataforge.names.parseAsName
import space.kscience.dataforge.names.startsWith
import kotlin.reflect.KProperty1
/**
* Call [callBack] on initial value of the property and then on all subsequent values after change
*/
public fun Vision.useProperty(
propertyName: Name,
inherit: Boolean? = null,
includeStyles: Boolean? = null,
scope: CoroutineScope? = manager?.context,
callBack: (Meta) -> Unit,
): Job {
//Pass initial value.
callBack(properties.getProperty(propertyName, inherit, includeStyles))
return properties.changes.onEach { name ->
if (name.startsWith(propertyName)) {
callBack(properties.getProperty(propertyName, inherit, includeStyles))
}
}.launchIn(scope ?: error("Orphan Vision can't observe properties"))
}
public fun Vision.useProperty(
propertyName: String,
inherit: Boolean? = null,
includeStyles: Boolean? = null,
scope: CoroutineScope? = manager?.context,
callBack: (Meta) -> Unit,
): Job = useProperty(propertyName.parseAsName(),inherit, includeStyles, scope, callBack)
public fun <V : Vision, T> V.useProperty(
property: KProperty1<V, T>,
scope: CoroutineScope? = manager?.context,
callBack: V.(T) -> Unit,
): Job {
//Pass initial value.
callBack(property.get(this))
return properties.changes.onEach { name ->
if (name.startsWith(property.name.asName())) {
callBack(property.get(this@useProperty))
}
}.launchIn(scope ?: error("Orphan Vision can't observe properties"))
}

View File

@ -30,7 +30,7 @@ fun FlowContent.renderVisionFragment(
@DFExperimental @DFExperimental
private fun VisionOutput.base(block: VisionGroup.() -> Unit) = VisionGroup().apply(block) private fun VisionOutput.base(block: VisionGroup.() -> Unit) = context.visionManager.group().apply(block)
@DFExperimental @DFExperimental
class HtmlTagTest { class HtmlTagTest {

View File

@ -1,10 +1,14 @@
package space.kscience.visionforge.meta package space.kscience.visionforge.meta
import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collectIndexed
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.runTest
import space.kscience.dataforge.context.Global
import space.kscience.dataforge.context.fetch
import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.*
import space.kscience.visionforge.VisionGroup import space.kscience.visionforge.*
import space.kscience.visionforge.getProperty
import space.kscience.visionforge.getValue
import space.kscience.visionforge.set
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertNotEquals import kotlin.test.assertNotEquals
@ -17,9 +21,12 @@ private class TestScheme : Scheme() {
} }
internal class VisionPropertyTest { internal class VisionPropertyTest {
private val manager = Global.fetch(VisionManager)
@Test @Test
fun testPropertyWrite() { fun testPropertyWrite() {
val vision = VisionGroup() val vision = manager.group()
vision.properties["fff"] = 2 vision.properties["fff"] = 2
vision.properties["fff.ddd"] = false vision.properties["fff.ddd"] = false
@ -29,7 +36,7 @@ internal class VisionPropertyTest {
@Test @Test
fun testPropertyEdit() { fun testPropertyEdit() {
val vision = VisionGroup() val vision = manager.group()
vision.properties.getProperty("fff.ddd").apply { vision.properties.getProperty("fff.ddd").apply {
value = 2.asValue() value = 2.asValue()
} }
@ -39,10 +46,78 @@ internal class VisionPropertyTest {
@Test @Test
fun testPropertyUpdate() { fun testPropertyUpdate() {
val vision = VisionGroup() val vision = manager.group()
vision.properties.getProperty("fff").updateWith(TestScheme) { vision.properties.getProperty("fff").updateWith(TestScheme) {
ddd = 2 ddd = 2
} }
assertEquals(2, vision.properties.getValue("fff.ddd")?.int) assertEquals(2, vision.properties.getValue("fff.ddd")?.int)
} }
@Test
fun testChildrenPropertyPropagation() = runTest(dispatchTimeoutMs = 200) {
val group = Global.fetch(VisionManager).group {
properties {
"test" put 11
}
group("child") {
properties {
"test" put 22
}
}
}
val child = group.children["child"]!!
var value: Value? = null
var callCounter = 0
child.useProperty("test", inherit = true) {
callCounter++
value = it.value
}
assertEquals(22, value?.int)
assertEquals(1, callCounter)
child.properties.remove("test")
//Need this to avoid the race
delay(20)
assertEquals(11, child.properties.getProperty("test", inherit = true).int)
assertEquals(11, value?.int)
assertEquals(2, callCounter)
}
@Test
fun testChildrenPropertyFlow() = runTest(dispatchTimeoutMs = 200) {
val group = Global.fetch(VisionManager).group {
properties {
"test" put 11
}
group("child") {
properties {
"test" put 22
}
}
}
val child = group.children["child"]!!
launch {
child.flowPropertyValue("test", inherit = true).collectIndexed { index, value ->
if (index == 0) {
assertEquals(22, value?.int)
} else if (index == 1) {
assertEquals(11, value?.int)
cancel()
}
}
}
//wait for subscription to be created
delay(10)
child.properties.remove("test")
}
} }

View File

@ -22,7 +22,7 @@ public class FXReferenceFactory(public val plugin: FX3DPlugin) : FX3DFactory<Sol
if (name.firstOrNull()?.body == REFERENCE_CHILD_PROPERTY_PREFIX) { if (name.firstOrNull()?.body == REFERENCE_CHILD_PROPERTY_PREFIX) {
val childName = name.firstOrNull()?.index?.let(Name::parse) ?: error("Wrong syntax for reference child property: '$name'") val childName = name.firstOrNull()?.index?.let(Name::parse) ?: error("Wrong syntax for reference child property: '$name'")
val propertyName = name.cutFirst() val propertyName = name.cutFirst()
val referenceChild = obj.children[childName] ?: error("Reference child with name '$childName' not found") val referenceChild = obj.children.getChild(childName) ?: error("Reference child with name '$childName' not found")
val child = node.findChild(childName) ?: error("Object child with name '$childName' not found") val child = node.findChild(childName) ?: error("Object child with name '$childName' not found")
child.updateProperty(referenceChild, propertyName) child.updateProperty(referenceChild, propertyName)
} }

View File

@ -62,7 +62,7 @@ private class GdmlLoader(val settings: GdmlLoaderOptions) {
): SolidReference { ): SolidReference {
val templateName = volumesName + volume.name.asName() val templateName = volumesName + volume.name.asName()
if (proto[templateName] == null) { if (proto[templateName] == null) {
proto[templateName] = volume(root, volume) proto.setChild(templateName, volume(root, volume))
} }
val ref = group.ref(templateName, physVolume.name).withPosition(root, physVolume) val ref = group.ref(templateName, physVolume.name).withPosition(root, physVolume)
referenceStore.getOrPut(templateName) { ArrayList() }.add(ref) referenceStore.getOrPut(templateName) { ArrayList() }.add(ref)
@ -302,7 +302,7 @@ private class GdmlLoader(val settings: GdmlLoaderOptions) {
when (settings.volumeAction(volume)) { when (settings.volumeAction(volume)) {
GdmlLoaderOptions.Action.ADD -> { GdmlLoaderOptions.Action.ADD -> {
val group: SolidGroup = volume(root, volume) val group: SolidGroup = volume(root, volume)
this[physVolume.name] = group.withPosition(root, physVolume) this.setChild(physVolume.name, group.withPosition(root, physVolume))
} }
GdmlLoaderOptions.Action.PROTOTYPE -> { GdmlLoaderOptions.Action.PROTOTYPE -> {
proxyVolume(root, this, physVolume, volume) proxyVolume(root, this, physVolume, volume)
@ -357,7 +357,7 @@ private class GdmlLoader(val settings: GdmlLoaderOptions) {
final.prototypes { final.prototypes {
proto.items.forEach { (token, item) -> proto.items.forEach { (token, item) ->
item.parent = null item.parent = null
set(token.asName(), item as? Solid) setChild(token.asName(), item as? Solid)
} }
} }
settings.styleCache.forEach { settings.styleCache.forEach {
@ -385,7 +385,7 @@ public fun Gdml.toVision(block: GdmlLoaderOptions.() -> Unit = {}): SolidGroup {
public fun SolidGroup.gdml(gdml: Gdml, key: String? = null, transformer: GdmlLoaderOptions.() -> Unit = {}) { public fun SolidGroup.gdml(gdml: Gdml, key: String? = null, transformer: GdmlLoaderOptions.() -> Unit = {}) {
val vision = gdml.toVision(transformer) val vision = gdml.toVision(transformer)
//println(Visual3DPlugin.json.stringify(VisualGroup3D.serializer(), visual)) //println(Visual3DPlugin.json.stringify(VisualGroup3D.serializer(), visual))
children[key] = vision children.setChild(key, vision)
} }
@VisionBuilder @VisionBuilder

View File

@ -4,7 +4,7 @@ import space.kscience.dataforge.context.Context
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.gdml.* import space.kscience.gdml.*
import space.kscience.visionforge.Vision import space.kscience.visionforge.Vision
import space.kscience.visionforge.get import space.kscience.visionforge.getChild
import space.kscience.visionforge.solid.* import space.kscience.visionforge.solid.*
import space.kscience.visionforge.visionManager import space.kscience.visionforge.visionManager
import kotlin.test.Test import kotlin.test.Test
@ -26,7 +26,7 @@ class TestCubes {
val smallBoxPrototype = vision.getPrototype(Name.parse("solids.smallBox")) as? Box val smallBoxPrototype = vision.getPrototype(Name.parse("solids.smallBox")) as? Box
assertNotNull(smallBoxPrototype) assertNotNull(smallBoxPrototype)
assertEquals(30.0, smallBoxPrototype.xSize.toDouble()) assertEquals(30.0, smallBoxPrototype.xSize.toDouble())
val smallBoxVision = vision.children["composite-111.smallBox"]?.prototype as? Box val smallBoxVision = vision.children.getChild("composite-111.smallBox")?.prototype as? Box
assertNotNull(smallBoxVision) assertNotNull(smallBoxVision)
assertEquals(30.0, smallBoxVision.xSize.toDouble()) assertEquals(30.0, smallBoxVision.xSize.toDouble())
} }

View File

@ -1,3 +1,5 @@
import space.kscience.gradle.KScienceVersions
plugins { plugins {
id("space.kscience.gradle.mpp") id("space.kscience.gradle.mpp")
} }
@ -17,7 +19,7 @@ kotlin {
} }
commonTest{ commonTest{
dependencies{ dependencies{
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:${KScienceVersions.coroutinesVersion}")
} }
} }
jvmTest{ jvmTest{

View File

@ -35,7 +35,7 @@ public inline fun MutableVisionContainer<Solid>.composite(
res.properties.setProperty(Name.EMPTY, group.properties.own) res.properties.setProperty(Name.EMPTY, group.properties.own)
set(name, res) setChild(name, res)
return res return res
} }
@ -57,7 +57,7 @@ public fun SolidGroup.smartComposite(
} }
this this
} else { } else {
children[name] = group children.setChild(name, group)
group group
} }
} else { } else {

View File

@ -4,7 +4,7 @@ import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import space.kscience.visionforge.MutableVisionContainer import space.kscience.visionforge.MutableVisionContainer
import space.kscience.visionforge.VisionBuilder import space.kscience.visionforge.VisionBuilder
import space.kscience.visionforge.set import space.kscience.visionforge.setChild
import kotlin.math.cos import kotlin.math.cos
import kotlin.math.sin import kotlin.math.sin
@ -76,7 +76,7 @@ public inline fun MutableVisionContainer<Solid>.cylinder(
r.toFloat(), r.toFloat(),
height.toFloat(), height.toFloat(),
r.toFloat() r.toFloat()
).apply(block).also { set(name, it) } ).apply(block).also { setChild(name, it) }
@VisionBuilder @VisionBuilder
public inline fun MutableVisionContainer<Solid>.cone( public inline fun MutableVisionContainer<Solid>.cone(
@ -93,4 +93,4 @@ public inline fun MutableVisionContainer<Solid>.cone(
topRadius = upperRadius.toFloat(), topRadius = upperRadius.toFloat(),
startAngle = startAngle.toFloat(), startAngle = startAngle.toFloat(),
angle = angle.toFloat() angle = angle.toFloat()
).apply(block).also { set(name, it) } ).apply(block).also { setChild(name, it) }

View File

@ -4,7 +4,7 @@ import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import space.kscience.visionforge.MutableVisionContainer import space.kscience.visionforge.MutableVisionContainer
import space.kscience.visionforge.VisionBuilder import space.kscience.visionforge.VisionBuilder
import space.kscience.visionforge.set import space.kscience.visionforge.setChild
import kotlin.math.PI import kotlin.math.PI
import kotlin.math.cos import kotlin.math.cos
import kotlin.math.sin import kotlin.math.sin
@ -139,7 +139,7 @@ public inline fun MutableVisionContainer<Solid>.tube(
topInnerRadius = innerRadius.toFloat(), topInnerRadius = innerRadius.toFloat(),
startAngle = startAngle.toFloat(), startAngle = startAngle.toFloat(),
angle = angle.toFloat() angle = angle.toFloat()
).apply(block).also { set(name, it) } ).apply(block).also { setChild(name, it) }
@VisionBuilder @VisionBuilder
public inline fun MutableVisionContainer<Solid>.coneSurface( public inline fun MutableVisionContainer<Solid>.coneSurface(
@ -160,4 +160,4 @@ public inline fun MutableVisionContainer<Solid>.coneSurface(
topInnerRadius = topInnerRadius.toFloat(), topInnerRadius = topInnerRadius.toFloat(),
startAngle = startAngle.toFloat(), startAngle = startAngle.toFloat(),
angle = angle.toFloat() angle = angle.toFloat()
).apply(block).also { set(name, it) } ).apply(block).also { setChild(name, it) }

View File

@ -3,7 +3,7 @@ package space.kscience.visionforge.solid
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import space.kscience.visionforge.MutableVisionContainer import space.kscience.visionforge.MutableVisionContainer
import space.kscience.visionforge.set import space.kscience.visionforge.setChild
@Serializable @Serializable
@SerialName("solid.convex") @SerialName("solid.convex")
@ -12,7 +12,7 @@ public class Convex(public val points: List<Point3D>) : SolidBase<Convex>()
public inline fun MutableVisionContainer<Solid>.convex( public inline fun MutableVisionContainer<Solid>.convex(
name: String? = null, name: String? = null,
action: ConvexBuilder.() -> Unit = {}, action: ConvexBuilder.() -> Unit = {},
): Convex = ConvexBuilder().apply(action).build().also { set(name, it) } ): Convex = ConvexBuilder().apply(action).build().also { setChild(name, it) }
public class ConvexBuilder { public class ConvexBuilder {
private val points = ArrayList<Point3D>() private val points = ArrayList<Point3D>()

View File

@ -115,4 +115,4 @@ public class ExtrudeBuilder(
public fun MutableVisionContainer<Solid>.extruded( public fun MutableVisionContainer<Solid>.extruded(
name: String? = null, name: String? = null,
action: ExtrudeBuilder.() -> Unit = {}, action: ExtrudeBuilder.() -> Unit = {},
): Extruded = ExtrudeBuilder().apply(action).build().also { set(name, it) } ): Extruded = ExtrudeBuilder().apply(action).build().also { setChild(name, it) }

View File

@ -4,7 +4,7 @@ import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import space.kscience.visionforge.MutableVisionContainer import space.kscience.visionforge.MutableVisionContainer
import space.kscience.visionforge.VisionBuilder import space.kscience.visionforge.VisionBuilder
import space.kscience.visionforge.set import space.kscience.visionforge.setChild
public interface Hexagon : GeometrySolid { public interface Hexagon : GeometrySolid {
public val node1: Point3D public val node1: Point3D
@ -58,7 +58,7 @@ public inline fun MutableVisionContainer<Solid>.box(
zSize: Number, zSize: Number,
name: String? = null, name: String? = null,
block: Box.() -> Unit = {}, block: Box.() -> Unit = {},
): Box = Box(xSize.toFloat(), ySize.toFloat(), zSize.toFloat()).apply(block).also { set(name, it) } ): Box = Box(xSize.toFloat(), ySize.toFloat(), zSize.toFloat()).apply(block).also { setChild(name, it) }
@Serializable @Serializable
@SerialName("solid.hexagon") @SerialName("solid.hexagon")
@ -85,4 +85,4 @@ public inline fun MutableVisionContainer<Solid>.hexagon(
node8: Point3D, node8: Point3D,
name: String? = null, name: String? = null,
action: Hexagon.() -> Unit = {}, action: Hexagon.() -> Unit = {},
): Hexagon = GenericHexagon(node1, node2, node3, node4, node5, node6, node7, node8).apply(action).also { set(name, it) } ): Hexagon = GenericHexagon(node1, node2, node3, node4, node5, node6, node7, node8).apply(action).also { setChild(name, it) }

View File

@ -55,7 +55,7 @@ public class AmbientLightSource : LightSource()
public fun MutableVisionContainer<Solid>.ambientLight( public fun MutableVisionContainer<Solid>.ambientLight(
name: String? = "@ambientLight", name: String? = "@ambientLight",
block: AmbientLightSource.() -> Unit = {}, block: AmbientLightSource.() -> Unit = {},
): AmbientLightSource = AmbientLightSource().apply(block).also { set(name, it) } ): AmbientLightSource = AmbientLightSource().apply(block).also { setChild(name, it) }
@Serializable @Serializable
@SerialName("solid.light.point") @SerialName("solid.light.point")
@ -71,5 +71,5 @@ public fun MutableVisionContainer<Solid>.pointLight(
block: PointLightSource.() -> Unit = {}, block: PointLightSource.() -> Unit = {},
): PointLightSource = PointLightSource().apply(block).also { ): PointLightSource = PointLightSource().apply(block).also {
it.position = Point3D(x, y, z) it.position = Point3D(x, y, z)
set(name, it) setChild(name, it)
} }

View File

@ -18,4 +18,4 @@ public fun MutableVisionContainer<Solid>.polyline(
vararg points: Point3D, vararg points: Point3D,
name: String? = null, name: String? = null,
action: PolyLine.() -> Unit = {}, action: PolyLine.() -> Unit = {},
): PolyLine = PolyLine(points.toList()).apply(action).also { set(name, it) } ): PolyLine = PolyLine(points.toList()).apply(action).also { setChild(name, it) }

View File

@ -38,7 +38,7 @@ public class SolidGroup : AbstractVisionGroup(), Solid, PrototypeHolder, Mutable
it to value it to value
}.toMap() }.toMap()
public operator fun get(name: Name): Solid? = children[name] as? Solid public operator fun get(name: Name): Solid? = children.getChild(name) as? Solid
private var prototypes: SolidGroup? private var prototypes: SolidGroup?
get() = items[PROTOTYPES_TOKEN] as? SolidGroup get() = items[PROTOTYPES_TOKEN] as? SolidGroup
@ -70,8 +70,8 @@ public class SolidGroup : AbstractVisionGroup(), Solid, PrototypeHolder, Mutable
// super.update(change) // super.update(change)
// } // }
override fun set(name: Name?, child: Solid?) { override fun setChild(name: Name?, child: Solid?) {
children[name] = child children.setChild(name, child)
} }
public companion object { public companion object {
@ -85,7 +85,7 @@ public inline fun SolidGroup(block: SolidGroup.() -> Unit): SolidGroup = SolidGr
public fun MutableVisionContainer<Solid>.group( public fun MutableVisionContainer<Solid>.group(
name: Name? = null, name: Name? = null,
builder: SolidGroup.() -> Unit = {}, builder: SolidGroup.() -> Unit = {},
): SolidGroup = SolidGroup(builder).also { set(name, it) } ): SolidGroup = SolidGroup(builder).also { setChild(name, it) }
/** /**
* Define a group with given [name], attach it to this parent and return it. * Define a group with given [name], attach it to this parent and return it.
@ -94,4 +94,4 @@ public fun MutableVisionContainer<Solid>.group(
public fun MutableVisionContainer<Solid>.group( public fun MutableVisionContainer<Solid>.group(
name: String, name: String,
action: SolidGroup.() -> Unit = {}, action: SolidGroup.() -> Unit = {},
): SolidGroup = SolidGroup(action).also { set(name, it) } ): SolidGroup = SolidGroup(action).also { setChild(name, it) }

View File

@ -4,7 +4,7 @@ import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import space.kscience.visionforge.MutableVisionContainer import space.kscience.visionforge.MutableVisionContainer
import space.kscience.visionforge.VisionBuilder import space.kscience.visionforge.VisionBuilder
import space.kscience.visionforge.set import space.kscience.visionforge.setChild
@Serializable @Serializable
@SerialName("solid.label") @SerialName("solid.label")
@ -21,4 +21,4 @@ public fun MutableVisionContainer<Solid>.label(
fontFamily: String = "Arial", fontFamily: String = "Arial",
name: String? = null, name: String? = null,
action: SolidLabel.() -> Unit = {}, action: SolidLabel.() -> Unit = {},
): SolidLabel = SolidLabel(text, fontSize.toDouble(), fontFamily).apply(action).also { set(name, it) } ): SolidLabel = SolidLabel(text, fontSize.toDouble(), fontFamily).apply(action).also { setChild(name, it) }

View File

@ -97,7 +97,7 @@ internal class SolidReferenceChild(
) : Solid, VisionGroup { ) : Solid, VisionGroup {
val prototype: Solid val prototype: Solid
get() = owner.prototype.children?.get(childName) as? Solid get() = owner.prototype.children?.getChild(childName) as? Solid
?: error("Prototype with name $childName not found") ?: error("Prototype with name $childName not found")
override val descriptor: MetaDescriptor get() = prototype.descriptor override val descriptor: MetaDescriptor get() = prototype.descriptor
@ -137,7 +137,7 @@ internal class SolidReferenceChild(
when { when {
change.delete -> error("Deleting children inside ref is not allowed.") change.delete -> error("Deleting children inside ref is not allowed.")
change.vision != null -> error("Updating content of the ref is not allowed") change.vision != null -> error("Updating content of the ref is not allowed")
else -> children[name]?.update(change) else -> children.getChild(name)?.update(change)
} }
} }
change.properties?.let { change.properties?.let {
@ -176,7 +176,7 @@ internal class SolidReferenceChild(
public fun MutableVisionContainer<Solid>.ref( public fun MutableVisionContainer<Solid>.ref(
templateName: Name, templateName: Name,
name: String? = null, name: String? = null,
): SolidReference = SolidReference(templateName).also { set(name, it) } ): SolidReference = SolidReference(templateName).also { setChild(name, it) }
public fun MutableVisionContainer<Solid>.ref( public fun MutableVisionContainer<Solid>.ref(
templateName: String, templateName: String,
@ -195,7 +195,7 @@ public fun SolidGroup.newRef(
val existing = prototypeHolder.getPrototype(prototypeName) val existing = prototypeHolder.getPrototype(prototypeName)
if (existing == null) { if (existing == null) {
prototypeHolder.prototypes { prototypeHolder.prototypes {
set(prototypeName, obj) setChild(prototypeName, obj)
} }
} else if (existing != obj) { } else if (existing != obj) {
error("Can't add different prototype on top of existing one") error("Can't add different prototype on top of existing one")

View File

@ -4,7 +4,7 @@ import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import space.kscience.visionforge.MutableVisionContainer import space.kscience.visionforge.MutableVisionContainer
import space.kscience.visionforge.VisionBuilder import space.kscience.visionforge.VisionBuilder
import space.kscience.visionforge.set import space.kscience.visionforge.setChild
import kotlin.math.PI import kotlin.math.PI
import kotlin.math.cos import kotlin.math.cos
import kotlin.math.sin import kotlin.math.sin
@ -58,4 +58,4 @@ public inline fun MutableVisionContainer<Solid>.sphere(
action: Sphere.() -> Unit = {}, action: Sphere.() -> Unit = {},
): Sphere = Sphere( ): Sphere = Sphere(
radius.toFloat(), radius.toFloat(),
).apply(action).also { set(name, it) } ).apply(action).also { setChild(name, it) }

View File

@ -4,7 +4,7 @@ import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import space.kscience.visionforge.MutableVisionContainer import space.kscience.visionforge.MutableVisionContainer
import space.kscience.visionforge.VisionBuilder import space.kscience.visionforge.VisionBuilder
import space.kscience.visionforge.set import space.kscience.visionforge.setChild
import kotlin.math.PI import kotlin.math.PI
import kotlin.math.cos import kotlin.math.cos
import kotlin.math.sin import kotlin.math.sin
@ -85,4 +85,4 @@ public inline fun MutableVisionContainer<Solid>.sphereLayer(
phi.toFloat(), phi.toFloat(),
thetaStart.toFloat(), thetaStart.toFloat(),
theta.toFloat() theta.toFloat()
).apply(action).also { set(name, it) } ).apply(action).also { setChild(name, it) }

View File

@ -39,7 +39,7 @@ internal object RemoveSingleChild : VisualTreeTransform<SolidGroup>() {
val child: Solid = parent.items.values.first() val child: Solid = parent.items.values.first()
val newParent = child.updateFrom(parent) val newParent = child.updateFrom(parent)
newParent.parent = null newParent.parent = null
children[childName.asName()] = newParent children.setChild(childName.asName(), newParent)
} }
} }
} }

View File

@ -4,14 +4,8 @@ import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.visionforge.solid.SolidGroup import space.kscience.visionforge.solid.SolidGroup
import space.kscience.visionforge.solid.SolidReference import space.kscience.visionforge.solid.SolidReference
import kotlin.collections.HashMap
import kotlin.collections.Map
import kotlin.collections.component1 import kotlin.collections.component1
import kotlin.collections.component2 import kotlin.collections.component2
import kotlin.collections.filter
import kotlin.collections.filterIsInstance
import kotlin.collections.fold
import kotlin.collections.forEach
import kotlin.collections.set import kotlin.collections.set
@DFExperimental @DFExperimental
@ -33,7 +27,7 @@ internal object UnRef : VisualTreeTransform<SolidGroup>() {
private fun SolidGroup.unref(name: Name) { private fun SolidGroup.unref(name: Name) {
(this as? SolidGroup)?.prototypes{ (this as? SolidGroup)?.prototypes{
set(name, null) setChild(name, null)
} }
items.filter { (it.value as? SolidReference)?.prototypeName == name }.forEach { (key, value) -> items.filter { (it.value as? SolidReference)?.prototypeName == name }.forEach { (key, value) ->
val reference = value as SolidReference val reference = value as SolidReference

View File

@ -1,7 +1,7 @@
package space.kscience.visionforge.solid package space.kscience.visionforge.solid
import space.kscience.visionforge.Colors import space.kscience.visionforge.Colors
import space.kscience.visionforge.get import space.kscience.visionforge.getChild
import kotlin.math.PI import kotlin.math.PI
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -45,7 +45,7 @@ class GroupTest {
} }
assertEquals(3, group.items.count()) assertEquals(3, group.items.count())
assertEquals(300.0, (group.children["intersect"] as Solid).y.toDouble()) assertEquals(300.0, (group.children.getChild("intersect") as Solid).y.toDouble())
assertEquals(-300.0, (group.children["subtract"] as Solid).y.toDouble()) assertEquals(-300.0, (group.children.getChild("subtract") as Solid).y.toDouble())
} }
} }

View File

@ -2,7 +2,7 @@ package space.kscience.visionforge.solid
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.visionforge.Colors import space.kscience.visionforge.Colors
import space.kscience.visionforge.get import space.kscience.visionforge.getChild
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -52,7 +52,7 @@ class SerializationTest {
val string = Solids.encodeToString(group) val string = Solids.encodeToString(group)
println(string) println(string)
val reconstructed = Solids.decodeFromString(string) as SolidGroup val reconstructed = Solids.decodeFromString(string) as SolidGroup
assertEquals(group.children["cube"]?.properties?.own, reconstructed.children["cube"]?.properties?.own) assertEquals(group.children.getChild("cube")?.properties?.own, reconstructed.children.getChild("cube")?.properties?.own)
} }
@Test @Test
@ -66,7 +66,7 @@ class SerializationTest {
val serialized = Solids.encodeToString(group) val serialized = Solids.encodeToString(group)
val reconstructed = Solids.decodeFromString(serialized) as SolidGroup val reconstructed = Solids.decodeFromString(serialized) as SolidGroup
assertEquals(100.0, (reconstructed.children["@ambientLight"] as AmbientLightSource).intensity.toDouble()) assertEquals(100.0, (reconstructed.children.getChild("@ambientLight") as AmbientLightSource).intensity.toDouble())
} }
} }

View File

@ -3,7 +3,7 @@ package space.kscience.visionforge.solid
import space.kscience.dataforge.context.Global import space.kscience.dataforge.context.Global
import space.kscience.dataforge.context.fetch import space.kscience.dataforge.context.fetch
import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.misc.DFExperimental
import space.kscience.visionforge.get import space.kscience.visionforge.getChild
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -25,8 +25,8 @@ class SolidPluginTest {
val reconstructed = visionManager.decodeFromMeta(meta) as SolidGroup val reconstructed = visionManager.decodeFromMeta(meta) as SolidGroup
assertEquals( assertEquals(
visionManager.encodeToJsonElement(vision.children["aBox"]!!), visionManager.encodeToJsonElement(vision.children.getChild("aBox")!!),
visionManager.encodeToJsonElement(reconstructed.children["aBox"]!!) visionManager.encodeToJsonElement(reconstructed.children.getChild("aBox")!!)
) )
} }
} }

View File

@ -3,7 +3,7 @@ package space.kscience.visionforge.solid
import kotlinx.serialization.json.encodeToJsonElement import kotlinx.serialization.json.encodeToJsonElement
import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.names.get import space.kscience.dataforge.names.get
import space.kscience.visionforge.get import space.kscience.visionforge.getChild
import space.kscience.visionforge.style import space.kscience.visionforge.style
import space.kscience.visionforge.useStyle import space.kscience.visionforge.useStyle
import kotlin.test.Test import kotlin.test.Test
@ -24,7 +24,7 @@ class SolidReferenceTest {
@Test @Test
fun testReferenceProperty(){ fun testReferenceProperty(){
assertEquals("blue", (groupWithReference.children["test"] as Solid).color.string) assertEquals("blue", (groupWithReference.children.getChild("test") as Solid).color.string)
} }
@Test @Test
@ -32,6 +32,6 @@ class SolidReferenceTest {
val serialized = Solids.jsonForSolids.encodeToJsonElement(groupWithReference) val serialized = Solids.jsonForSolids.encodeToJsonElement(groupWithReference)
val deserialized = Solids.jsonForSolids.decodeFromJsonElement(SolidGroup.serializer(), serialized) val deserialized = Solids.jsonForSolids.decodeFromJsonElement(SolidGroup.serializer(), serialized)
assertEquals(groupWithReference.items["test"]?.color.string, deserialized.items["test"]?.color.string) assertEquals(groupWithReference.items["test"]?.color.string, deserialized.items["test"]?.color.string)
assertEquals("blue", (deserialized.children["test"] as Solid).color.string) assertEquals("blue", (deserialized.children.getChild("test") as Solid).color.string)
} }
} }

View File

@ -6,7 +6,7 @@ import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.asValue import space.kscience.dataforge.meta.asValue
import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.asName
import space.kscience.visionforge.VisionChange import space.kscience.visionforge.VisionChange
import space.kscience.visionforge.get import space.kscience.visionforge.getChild
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertTrue import kotlin.test.assertTrue
@ -29,9 +29,9 @@ internal class VisionUpdateTest {
propertyChanged("origin".asName(), SolidMaterial.MATERIAL_COLOR_KEY, Meta("red".asValue())) propertyChanged("origin".asName(), SolidMaterial.MATERIAL_COLOR_KEY, Meta("red".asValue()))
} }
targetVision.update(dif) targetVision.update(dif)
assertTrue { targetVision.children["top"] is SolidGroup } assertTrue { targetVision.children.getChild("top") is SolidGroup }
assertEquals("red", (targetVision.children["origin"] as Solid).color.string) // Should work assertEquals("red", (targetVision.children.getChild("origin") as Solid).color.string) // Should work
assertEquals("#00007b", (targetVision.children["top"] as Solid).color.string) // new item always takes precedence assertEquals("#00007b", (targetVision.children.getChild("top") as Solid).color.string) // new item always takes precedence
} }
@Test @Test

View File

@ -10,6 +10,6 @@ kotlin{
dependencies { dependencies {
api(project(":visionforge-solid")) api(project(":visionforge-solid"))
implementation(npm("three", "0.137.4")) implementation(npm("three", "0.137.5"))
implementation(npm("three-csg-ts", "3.1.10")) implementation(npm("three-csg-ts", "3.1.10"))
} }

View File

@ -4,7 +4,7 @@ import info.laht.threekt.geometries.BoxGeometry
import space.kscience.visionforge.solid.Box import space.kscience.visionforge.solid.Box
import space.kscience.visionforge.solid.detail import space.kscience.visionforge.solid.detail
public object ThreeBoxFactory : MeshThreeFactory<Box>(Box::class) { public object ThreeBoxFactory : ThreeMeshFactory<Box>(Box::class) {
override fun buildGeometry(obj: Box): BoxGeometry = override fun buildGeometry(obj: Box): BoxGeometry =
obj.detail?.let { detail -> obj.detail?.let { detail ->
BoxGeometry(obj.xSize, obj.ySize, obj.zSize, detail, detail, detail) BoxGeometry(obj.xSize, obj.ySize, obj.zSize, detail, detail, detail)

View File

@ -50,7 +50,7 @@ public class ThreeCompositeFactory(public val three: ThreePlugin) : ThreeFactory
obj.onPropertyChange { name -> obj.onPropertyChange { name ->
when { when {
//name.startsWith(WIREFRAME_KEY) -> mesh.applyWireFrame(obj) //name.startsWith(WIREFRAME_KEY) -> mesh.applyWireFrame(obj)
name.startsWith(MeshThreeFactory.EDGES_KEY) -> applyEdges(obj) name.startsWith(ThreeMeshFactory.EDGES_KEY) -> applyEdges(obj)
else -> updateProperty(obj, name) else -> updateProperty(obj, name)
} }
} }

View File

@ -7,7 +7,7 @@ import space.kscience.visionforge.solid.detail
import kotlin.math.PI import kotlin.math.PI
import kotlin.math.pow import kotlin.math.pow
public object ThreeConeFactory : MeshThreeFactory<ConeSegment>(ConeSegment::class) { public object ThreeConeFactory : ThreeMeshFactory<ConeSegment>(ConeSegment::class) {
override fun buildGeometry(obj: ConeSegment): BufferGeometry { override fun buildGeometry(obj: ConeSegment): BufferGeometry {
val cylinder = obj.detail?.let { val cylinder = obj.detail?.let {
val segments = it.toDouble().pow(0.5).toInt() val segments = it.toDouble().pow(0.5).toInt()

View File

@ -3,7 +3,7 @@ package space.kscience.visionforge.solid.three
import info.laht.threekt.external.geometries.ConvexBufferGeometry import info.laht.threekt.external.geometries.ConvexBufferGeometry
import space.kscience.visionforge.solid.Convex import space.kscience.visionforge.solid.Convex
public object ThreeConvexFactory : MeshThreeFactory<Convex>(Convex::class) { public object ThreeConvexFactory : ThreeMeshFactory<Convex>(Convex::class) {
override fun buildGeometry(obj: Convex): ConvexBufferGeometry { override fun buildGeometry(obj: Convex): ConvexBufferGeometry {
val vectors = obj.points.map { it.toVector() }.toTypedArray() val vectors = obj.points.map { it.toVector() }.toTypedArray()
return ConvexBufferGeometry(vectors) return ConvexBufferGeometry(vectors)

View File

@ -75,7 +75,7 @@ public fun Object3D.updateProperty(source: Vision, propertyName: Name) {
/** /**
* Generic factory for elements which provide inside geometry builder * Generic factory for elements which provide inside geometry builder
*/ */
public object ThreeShapeFactory : MeshThreeFactory<GeometrySolid>(GeometrySolid::class) { public object ThreeShapeFactory : ThreeMeshFactory<GeometrySolid>(GeometrySolid::class) {
override fun buildGeometry(obj: GeometrySolid): BufferGeometry = ThreeGeometryBuilder().apply { override fun buildGeometry(obj: GeometrySolid): BufferGeometry = ThreeGeometryBuilder().apply {
obj.toGeometry(this) obj.toGeometry(this)
}.build() }.build()

View File

@ -15,14 +15,14 @@ import space.kscience.visionforge.set
import space.kscience.visionforge.solid.Solid import space.kscience.visionforge.solid.Solid
import space.kscience.visionforge.solid.SolidMaterial import space.kscience.visionforge.solid.SolidMaterial
import space.kscience.visionforge.solid.layer import space.kscience.visionforge.solid.layer
import space.kscience.visionforge.solid.three.MeshThreeFactory.Companion.EDGES_ENABLED_KEY import space.kscience.visionforge.solid.three.ThreeMeshFactory.Companion.EDGES_ENABLED_KEY
import space.kscience.visionforge.solid.three.MeshThreeFactory.Companion.EDGES_MATERIAL_KEY import space.kscience.visionforge.solid.three.ThreeMeshFactory.Companion.EDGES_MATERIAL_KEY
import kotlin.reflect.KClass import kotlin.reflect.KClass
/** /**
* Basic geometry-based factory * Basic geometry-based factory
*/ */
public abstract class MeshThreeFactory<in T : Solid>( public abstract class ThreeMeshFactory<in T : Solid>(
override val type: KClass<in T>, override val type: KClass<in T>,
) : ThreeFactory<T> { ) : ThreeFactory<T> {
/** /**

View File

@ -82,7 +82,7 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
}.launchIn(context) }.launchIn(context)
obj.children.changes.onEach { childName -> obj.children.changes.onEach { childName ->
val child = obj.children[childName] val child = obj.children.getChild(childName)
//removing old object //removing old object
findChild(childName)?.let { oldChild -> findChild(childName)?.let { oldChild ->

View File

@ -53,7 +53,7 @@ public object ThreeReferenceFactory : ThreeFactory<SolidReference> {
?: error("Wrong syntax for reference child property: '$name'") ?: error("Wrong syntax for reference child property: '$name'")
val propertyName = name.cutFirst() val propertyName = name.cutFirst()
val referenceChild = val referenceChild =
obj.children[childName] ?: error("Reference child with name '$childName' not found") obj.children.getChild(childName) ?: error("Reference child with name '$childName' not found")
val child = object3D.findChild(childName) ?: error("Object child with name '$childName' not found") val child = object3D.findChild(childName) ?: error("Object child with name '$childName' not found")
child.updateProperty(referenceChild, propertyName) child.updateProperty(referenceChild, propertyName)
} else { } else {

View File

@ -5,7 +5,7 @@ import info.laht.threekt.geometries.SphereGeometry
import space.kscience.visionforge.solid.Sphere import space.kscience.visionforge.solid.Sphere
import space.kscience.visionforge.solid.detail import space.kscience.visionforge.solid.detail
public object ThreeSphereFactory : MeshThreeFactory<Sphere>(Sphere::class) { public object ThreeSphereFactory : ThreeMeshFactory<Sphere>(Sphere::class) {
override fun buildGeometry(obj: Sphere): BufferGeometry { override fun buildGeometry(obj: Sphere): BufferGeometry {
return obj.detail?.let {detail -> return obj.detail?.let {detail ->
SphereGeometry( SphereGeometry(