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()
}
set(combinedName?.let { Name.parse(it) }, group)
setChild(combinedName?.let { Name.parse(it) }, group)
} else {
val templateName = volumesName + volume.name
val existing = getPrototype(templateName)
if (existing == null) {
context.prototypeHolder.prototypes {
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.gdml.GdmlShowCase
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.SolidMaterial
import space.kscience.visionforge.solid.material
@ -18,7 +18,7 @@ class GDMLVisionTest {
@Test
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.computePropertyNode(SolidMaterial.MATERIAL_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-content-negotiation:${ktorVersion}")
implementation("io.ktor:ktor-serialization-kotlinx-json:${ktorVersion}")
implementation("ch.qos.logback:logback-classic:1.2.11")
}
}
jsMain {

View File

@ -3,7 +3,10 @@ package space.kscience.visionforge.solid.demo
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.names.Name
import space.kscience.visionforge.Vision
import space.kscience.visionforge.solid.Solids
public interface VisionLayout<in V: Vision> {
val solids: Solids
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.names.Name
import space.kscience.visionforge.Colors
import space.kscience.visionforge.setAsRoot
import space.kscience.visionforge.solid.*
import space.kscience.visionforge.solid.specifications.Canvas3DOptions
import space.kscience.visionforge.visible
@ -22,6 +23,7 @@ fun VisionLayout<Solid>.demo(name: String, title: String = name, block: SolidGro
ambientLight{
color.set(Colors.white)
}
setAsRoot(solids.visionManager)
}
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.names.Name
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.ThreePlugin
@ -27,6 +28,8 @@ class ThreeDemoGrid(element: Element) : VisionLayout<Solid> {
private val three = Global.fetch(ThreePlugin)
override val solids: Solids get() = three.solids
init {
element.clear()
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.startsWith
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.layer
import space.kscience.visionforge.solid.three.*
@ -20,7 +20,7 @@ internal fun SolidGroup.varBox(
ySize: Number,
name: String = "",
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() {
@ -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)
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)
}
}

View File

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

View File

@ -13,6 +13,11 @@ kotlin {
api("org.jetbrains.kotlin-wrappers:kotlin-css")
}
}
commonTest{
dependencies{
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:${space.kscience.gradle.KScienceVersions.coroutinesVersion}")
}
}
jsMain {
dependencies {
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.names.Name
import space.kscience.dataforge.names.asName
import space.kscience.dataforge.names.startsWith
import space.kscience.visionforge.AbstractVisionGroup.Companion.updateProperties
import space.kscience.visionforge.Vision.Companion.TYPE
import kotlin.reflect.KProperty1
/**
* A root type for display hierarchy
@ -73,18 +71,3 @@ public fun Vision.onPropertyChange(
): Job = properties.changes.onEach {
callback(it)
}.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")
getOrPutChild(name).apply {
vision = child
@ -109,7 +109,7 @@ private fun CoroutineScope.collectChange(
if (after != null) {
collectChange(fullName, after, collector)
}
collector()[fullName] = after
collector().setChild(fullName, after)
}?.launchIn(this)
}

View File

@ -11,12 +11,12 @@ import kotlin.jvm.Synchronized
public annotation class VisionBuilder
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> {
//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?
override fun get(name: Name): Vision? = when (name.length) {
override fun getChild(name: Name): Vision? = when (name.length) {
0 -> group
1 -> get(name.first())
else -> get(name.first())?.children?.get(name.cutFirst())
else -> get(name.first())?.children?.getChild(name.cutFirst())
}
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 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?)
override fun set(name: Name?, child: Vision?) {
override fun setChild(name: Name?, child: Vision?) {
when {
name == null -> {
if (child != null) {
@ -81,7 +85,7 @@ public interface MutableVisionChildren : VisionChildren, MutableVisionContainer<
val parent: MutableVisionGroup = currentParent as? MutableVisionGroup ?: group.createGroup().also {
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 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.
*/
@ -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 <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?,
): Unit = set(str?.parseAsName(), vision)
): Unit = setChild(str?.parseAsName(), vision)
internal abstract class VisionChildrenImpl(
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.plus
import space.kscience.visionforge.Vision.Companion.STYLE_KEY
import kotlin.js.JsName
public interface VisionGroup : Vision {
@ -35,9 +34,9 @@ public abstract class AbstractVisionGroup : AbstractVision(), MutableVisionGroup
override fun update(change: VisionChange) {
change.children?.forEach { (name, change) ->
when {
change.delete -> children[name] = null
change.vision != null -> children[name] = change.vision
else -> children[name]?.update(change)
change.delete -> children.setChild(name, null)
change.vision != null -> children.setChild(name, change.vision)
else -> children.getChild(name)?.update(change)
}
}
change.properties?.let {
@ -87,12 +86,28 @@ public abstract class AbstractVisionGroup : AbstractVision(), MutableVisionGroup
*/
@Serializable
@SerialName("vision.group")
public class SimpleVisionGroup : AbstractVisionGroup() {
public class SimpleVisionGroup : AbstractVisionGroup(), MutableVisionContainer<Vision> {
override fun createGroup(): SimpleVisionGroup = SimpleVisionGroup()
override fun setChild(name: Name?, child: Vision?) {
children.setChild(name, child)
}
}
@JsName("createVisionGroup")
public fun VisionGroup(): VisionGroup = SimpleVisionGroup()
@VisionBuilder
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? {
// if (this is VisualGroup) {

View File

@ -19,7 +19,7 @@ import space.kscience.visionforge.html.VisionOfNumberField
import space.kscience.visionforge.html.VisionOfTextField
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
/**
@ -58,6 +58,10 @@ public class VisionManager(meta: Meta) : AbstractPlugin(meta) {
public fun encodeToMeta(vision: Vision, descriptor: MetaDescriptor? = null): Meta =
encodeToJsonElement(vision).toMeta(descriptor)
override fun setChild(name: Name?, child: Vision?) {
child?.setAsRoot(this)
}
public companion object : PluginFactory<VisionManager> {
override val tag: PluginTag = PluginTag(name = "vision", group = PluginTag.DATAFORGE_GROUP)
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(
val properties: MutableVisionProperties,
val nodeName: Name,
@ -190,7 +203,7 @@ public abstract class AbstractVisionProperties(
}
@Transient
private val _changes = MutableSharedFlow<Name>(10)
private val _changes = MutableSharedFlow<Name>()
override val changes: SharedFlow<Name> get() = _changes
@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
private fun VisionOutput.base(block: VisionGroup.() -> Unit) = VisionGroup().apply(block)
private fun VisionOutput.base(block: VisionGroup.() -> Unit) = context.visionManager.group().apply(block)
@DFExperimental
class HtmlTagTest {

View File

@ -1,10 +1,14 @@
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.visionforge.VisionGroup
import space.kscience.visionforge.getProperty
import space.kscience.visionforge.getValue
import space.kscience.visionforge.set
import space.kscience.visionforge.*
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotEquals
@ -17,9 +21,12 @@ private class TestScheme : Scheme() {
}
internal class VisionPropertyTest {
private val manager = Global.fetch(VisionManager)
@Test
fun testPropertyWrite() {
val vision = VisionGroup()
val vision = manager.group()
vision.properties["fff"] = 2
vision.properties["fff.ddd"] = false
@ -29,7 +36,7 @@ internal class VisionPropertyTest {
@Test
fun testPropertyEdit() {
val vision = VisionGroup()
val vision = manager.group()
vision.properties.getProperty("fff.ddd").apply {
value = 2.asValue()
}
@ -39,10 +46,78 @@ internal class VisionPropertyTest {
@Test
fun testPropertyUpdate() {
val vision = VisionGroup()
val vision = manager.group()
vision.properties.getProperty("fff").updateWith(TestScheme) {
ddd = 2
}
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) {
val childName = name.firstOrNull()?.index?.let(Name::parse) ?: error("Wrong syntax for reference child property: '$name'")
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")
child.updateProperty(referenceChild, propertyName)
}

View File

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

View File

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

View File

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

View File

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

View File

@ -4,7 +4,7 @@ import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import space.kscience.visionforge.MutableVisionContainer
import space.kscience.visionforge.VisionBuilder
import space.kscience.visionforge.set
import space.kscience.visionforge.setChild
import kotlin.math.cos
import kotlin.math.sin
@ -76,7 +76,7 @@ public inline fun MutableVisionContainer<Solid>.cylinder(
r.toFloat(),
height.toFloat(),
r.toFloat()
).apply(block).also { set(name, it) }
).apply(block).also { setChild(name, it) }
@VisionBuilder
public inline fun MutableVisionContainer<Solid>.cone(
@ -93,4 +93,4 @@ public inline fun MutableVisionContainer<Solid>.cone(
topRadius = upperRadius.toFloat(),
startAngle = startAngle.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 space.kscience.visionforge.MutableVisionContainer
import space.kscience.visionforge.VisionBuilder
import space.kscience.visionforge.set
import space.kscience.visionforge.setChild
import kotlin.math.PI
import kotlin.math.cos
import kotlin.math.sin
@ -139,7 +139,7 @@ public inline fun MutableVisionContainer<Solid>.tube(
topInnerRadius = innerRadius.toFloat(),
startAngle = startAngle.toFloat(),
angle = angle.toFloat()
).apply(block).also { set(name, it) }
).apply(block).also { setChild(name, it) }
@VisionBuilder
public inline fun MutableVisionContainer<Solid>.coneSurface(
@ -160,4 +160,4 @@ public inline fun MutableVisionContainer<Solid>.coneSurface(
topInnerRadius = topInnerRadius.toFloat(),
startAngle = startAngle.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.Serializable
import space.kscience.visionforge.MutableVisionContainer
import space.kscience.visionforge.set
import space.kscience.visionforge.setChild
@Serializable
@SerialName("solid.convex")
@ -12,7 +12,7 @@ public class Convex(public val points: List<Point3D>) : SolidBase<Convex>()
public inline fun MutableVisionContainer<Solid>.convex(
name: String? = null,
action: ConvexBuilder.() -> Unit = {},
): Convex = ConvexBuilder().apply(action).build().also { set(name, it) }
): Convex = ConvexBuilder().apply(action).build().also { setChild(name, it) }
public class ConvexBuilder {
private val points = ArrayList<Point3D>()

View File

@ -115,4 +115,4 @@ public class ExtrudeBuilder(
public fun MutableVisionContainer<Solid>.extruded(
name: String? = null,
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 space.kscience.visionforge.MutableVisionContainer
import space.kscience.visionforge.VisionBuilder
import space.kscience.visionforge.set
import space.kscience.visionforge.setChild
public interface Hexagon : GeometrySolid {
public val node1: Point3D
@ -58,7 +58,7 @@ public inline fun MutableVisionContainer<Solid>.box(
zSize: Number,
name: String? = null,
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
@SerialName("solid.hexagon")
@ -85,4 +85,4 @@ public inline fun MutableVisionContainer<Solid>.hexagon(
node8: Point3D,
name: String? = null,
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(
name: String? = "@ambientLight",
block: AmbientLightSource.() -> Unit = {},
): AmbientLightSource = AmbientLightSource().apply(block).also { set(name, it) }
): AmbientLightSource = AmbientLightSource().apply(block).also { setChild(name, it) }
@Serializable
@SerialName("solid.light.point")
@ -71,5 +71,5 @@ public fun MutableVisionContainer<Solid>.pointLight(
block: PointLightSource.() -> Unit = {},
): PointLightSource = PointLightSource().apply(block).also {
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,
name: String? = null,
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
}.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?
get() = items[PROTOTYPES_TOKEN] as? SolidGroup
@ -70,8 +70,8 @@ public class SolidGroup : AbstractVisionGroup(), Solid, PrototypeHolder, Mutable
// super.update(change)
// }
override fun set(name: Name?, child: Solid?) {
children[name] = child
override fun setChild(name: Name?, child: Solid?) {
children.setChild(name, child)
}
public companion object {
@ -85,7 +85,7 @@ public inline fun SolidGroup(block: SolidGroup.() -> Unit): SolidGroup = SolidGr
public fun MutableVisionContainer<Solid>.group(
name: Name? = null,
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.
@ -94,4 +94,4 @@ public fun MutableVisionContainer<Solid>.group(
public fun MutableVisionContainer<Solid>.group(
name: String,
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 space.kscience.visionforge.MutableVisionContainer
import space.kscience.visionforge.VisionBuilder
import space.kscience.visionforge.set
import space.kscience.visionforge.setChild
@Serializable
@SerialName("solid.label")
@ -21,4 +21,4 @@ public fun MutableVisionContainer<Solid>.label(
fontFamily: String = "Arial",
name: String? = null,
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 {
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")
override val descriptor: MetaDescriptor get() = prototype.descriptor
@ -137,7 +137,7 @@ internal class SolidReferenceChild(
when {
change.delete -> error("Deleting children inside 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 {
@ -176,7 +176,7 @@ internal class SolidReferenceChild(
public fun MutableVisionContainer<Solid>.ref(
templateName: Name,
name: String? = null,
): SolidReference = SolidReference(templateName).also { set(name, it) }
): SolidReference = SolidReference(templateName).also { setChild(name, it) }
public fun MutableVisionContainer<Solid>.ref(
templateName: String,
@ -195,7 +195,7 @@ public fun SolidGroup.newRef(
val existing = prototypeHolder.getPrototype(prototypeName)
if (existing == null) {
prototypeHolder.prototypes {
set(prototypeName, obj)
setChild(prototypeName, obj)
}
} else if (existing != obj) {
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 space.kscience.visionforge.MutableVisionContainer
import space.kscience.visionforge.VisionBuilder
import space.kscience.visionforge.set
import space.kscience.visionforge.setChild
import kotlin.math.PI
import kotlin.math.cos
import kotlin.math.sin
@ -58,4 +58,4 @@ public inline fun MutableVisionContainer<Solid>.sphere(
action: Sphere.() -> Unit = {},
): Sphere = Sphere(
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 space.kscience.visionforge.MutableVisionContainer
import space.kscience.visionforge.VisionBuilder
import space.kscience.visionforge.set
import space.kscience.visionforge.setChild
import kotlin.math.PI
import kotlin.math.cos
import kotlin.math.sin
@ -85,4 +85,4 @@ public inline fun MutableVisionContainer<Solid>.sphereLayer(
phi.toFloat(),
thetaStart.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 newParent = child.updateFrom(parent)
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.visionforge.solid.SolidGroup
import space.kscience.visionforge.solid.SolidReference
import kotlin.collections.HashMap
import kotlin.collections.Map
import kotlin.collections.component1
import kotlin.collections.component2
import kotlin.collections.filter
import kotlin.collections.filterIsInstance
import kotlin.collections.fold
import kotlin.collections.forEach
import kotlin.collections.set
@DFExperimental
@ -33,7 +27,7 @@ internal object UnRef : VisualTreeTransform<SolidGroup>() {
private fun SolidGroup.unref(name: Name) {
(this as? SolidGroup)?.prototypes{
set(name, null)
setChild(name, null)
}
items.filter { (it.value as? SolidReference)?.prototypeName == name }.forEach { (key, value) ->
val reference = value as SolidReference

View File

@ -1,7 +1,7 @@
package space.kscience.visionforge.solid
import space.kscience.visionforge.Colors
import space.kscience.visionforge.get
import space.kscience.visionforge.getChild
import kotlin.math.PI
import kotlin.test.Test
import kotlin.test.assertEquals
@ -45,7 +45,7 @@ class GroupTest {
}
assertEquals(3, group.items.count())
assertEquals(300.0, (group.children["intersect"] as Solid).y.toDouble())
assertEquals(-300.0, (group.children["subtract"] as Solid).y.toDouble())
assertEquals(300.0, (group.children.getChild("intersect") 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.visionforge.Colors
import space.kscience.visionforge.get
import space.kscience.visionforge.getChild
import kotlin.test.Test
import kotlin.test.assertEquals
@ -52,7 +52,7 @@ class SerializationTest {
val string = Solids.encodeToString(group)
println(string)
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
@ -66,7 +66,7 @@ class SerializationTest {
val serialized = Solids.encodeToString(group)
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.fetch
import space.kscience.dataforge.misc.DFExperimental
import space.kscience.visionforge.get
import space.kscience.visionforge.getChild
import kotlin.test.Test
import kotlin.test.assertEquals
@ -25,8 +25,8 @@ class SolidPluginTest {
val reconstructed = visionManager.decodeFromMeta(meta) as SolidGroup
assertEquals(
visionManager.encodeToJsonElement(vision.children["aBox"]!!),
visionManager.encodeToJsonElement(reconstructed.children["aBox"]!!)
visionManager.encodeToJsonElement(vision.children.getChild("aBox")!!),
visionManager.encodeToJsonElement(reconstructed.children.getChild("aBox")!!)
)
}
}

View File

@ -3,7 +3,7 @@ package space.kscience.visionforge.solid
import kotlinx.serialization.json.encodeToJsonElement
import space.kscience.dataforge.misc.DFExperimental
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.useStyle
import kotlin.test.Test
@ -24,7 +24,7 @@ class SolidReferenceTest {
@Test
fun testReferenceProperty(){
assertEquals("blue", (groupWithReference.children["test"] as Solid).color.string)
assertEquals("blue", (groupWithReference.children.getChild("test") as Solid).color.string)
}
@Test
@ -32,6 +32,6 @@ class SolidReferenceTest {
val serialized = Solids.jsonForSolids.encodeToJsonElement(groupWithReference)
val deserialized = Solids.jsonForSolids.decodeFromJsonElement(SolidGroup.serializer(), serialized)
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.names.asName
import space.kscience.visionforge.VisionChange
import space.kscience.visionforge.get
import space.kscience.visionforge.getChild
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue
@ -29,9 +29,9 @@ internal class VisionUpdateTest {
propertyChanged("origin".asName(), SolidMaterial.MATERIAL_COLOR_KEY, Meta("red".asValue()))
}
targetVision.update(dif)
assertTrue { targetVision.children["top"] is SolidGroup }
assertEquals("red", (targetVision.children["origin"] as Solid).color.string) // Should work
assertEquals("#00007b", (targetVision.children["top"] as Solid).color.string) // new item always takes precedence
assertTrue { targetVision.children.getChild("top") is SolidGroup }
assertEquals("red", (targetVision.children.getChild("origin") as Solid).color.string) // Should work
assertEquals("#00007b", (targetVision.children.getChild("top") as Solid).color.string) // new item always takes precedence
}
@Test

View File

@ -10,6 +10,6 @@ kotlin{
dependencies {
api(project(":visionforge-solid"))
implementation(npm("three", "0.137.4"))
implementation(npm("three", "0.137.5"))
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.detail
public object ThreeBoxFactory : MeshThreeFactory<Box>(Box::class) {
public object ThreeBoxFactory : ThreeMeshFactory<Box>(Box::class) {
override fun buildGeometry(obj: Box): BoxGeometry =
obj.detail?.let { 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 ->
when {
//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)
}
}

View File

@ -7,7 +7,7 @@ import space.kscience.visionforge.solid.detail
import kotlin.math.PI
import kotlin.math.pow
public object ThreeConeFactory : MeshThreeFactory<ConeSegment>(ConeSegment::class) {
public object ThreeConeFactory : ThreeMeshFactory<ConeSegment>(ConeSegment::class) {
override fun buildGeometry(obj: ConeSegment): BufferGeometry {
val cylinder = obj.detail?.let {
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 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 {
val vectors = obj.points.map { it.toVector() }.toTypedArray()
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
*/
public object ThreeShapeFactory : MeshThreeFactory<GeometrySolid>(GeometrySolid::class) {
public object ThreeShapeFactory : ThreeMeshFactory<GeometrySolid>(GeometrySolid::class) {
override fun buildGeometry(obj: GeometrySolid): BufferGeometry = ThreeGeometryBuilder().apply {
obj.toGeometry(this)
}.build()

View File

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

View File

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

View File

@ -53,7 +53,7 @@ public object ThreeReferenceFactory : ThreeFactory<SolidReference> {
?: error("Wrong syntax for reference child property: '$name'")
val propertyName = name.cutFirst()
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")
child.updateProperty(referenceChild, propertyName)
} else {

View File

@ -5,7 +5,7 @@ import info.laht.threekt.geometries.SphereGeometry
import space.kscience.visionforge.solid.Sphere
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 {
return obj.detail?.let {detail ->
SphereGeometry(