diff --git a/build.gradle.kts b/build.gradle.kts index 5e8bc323..f0c9d504 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,7 +3,7 @@ plugins { // id("org.jetbrains.kotlinx.kover") version "0.5.0" } -val dataforgeVersion by extra("0.6.0-dev-10") +val dataforgeVersion by extra("0.6.0-dev-12") val fxVersion by extra("11") allprojects{ diff --git a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/dRootToSolid.kt b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/dRootToSolid.kt index 7c345dd4..fbeb84dd 100644 --- a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/dRootToSolid.kt +++ b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/dRootToSolid.kt @@ -1,10 +1,14 @@ package ru.mipt.npm.root -import space.kscience.dataforge.meta.* +import space.kscience.dataforge.meta.double +import space.kscience.dataforge.meta.get +import space.kscience.dataforge.meta.int +import space.kscience.dataforge.meta.isEmpty import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.plus import space.kscience.dataforge.values.doubleArray import space.kscience.visionforge.isEmpty +import space.kscience.visionforge.setPropertyValue import space.kscience.visionforge.solid.* import space.kscience.visionforge.solid.SolidMaterial.Companion.MATERIAL_COLOR_KEY import kotlin.math.* @@ -20,7 +24,7 @@ private fun degToRad(d: Double) = d * PI / 180.0 private data class RootToSolidContext( val prototypeHolder: PrototypeHolder, val currentLayer: Int = 0, - val maxLayer: Int = 5 + val maxLayer: Int = 5, ) // converting to XYZ to Tait–Bryan angles according to https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix @@ -42,14 +46,17 @@ private fun Solid.useMatrix(matrix: DGeoMatrix?) { "TGeoIdentity" -> { //do nothing } + "TGeoTranslation" -> { val fTranslation by matrix.meta.doubleArray() translate(fTranslation) } + "TGeoRotation" -> { val fRotationMatrix by matrix.meta.doubleArray() rotate(fRotationMatrix) } + "TGeoCombiTrans" -> { val fTranslation by matrix.meta.doubleArray() @@ -58,6 +65,7 @@ private fun Solid.useMatrix(matrix: DGeoMatrix?) { rotate(it.doubleArray) } } + "TGeoHMatrix" -> { val fTranslation by matrix.meta.doubleArray() val fRotationMatrix by matrix.meta.doubleArray() @@ -73,7 +81,7 @@ private fun SolidGroup.addShape( shape: DGeoShape, context: RootToSolidContext, name: String? = shape.fName.ifEmpty { null }, - block: Solid.() -> Unit = {} + block: Solid.() -> Unit = {}, ) { when (shape.typename) { "TGeoCompositeShape" -> { @@ -94,6 +102,7 @@ private fun SolidGroup.addShape( } }.apply(block) } + "TGeoXtru" -> { val fNvert by shape.meta.int(0) val fX by shape.meta.doubleArray() @@ -121,6 +130,7 @@ private fun SolidGroup.addShape( } }.apply(block) } + "TGeoTube" -> { val fRmax by shape.meta.double(0.0) val fDz by shape.meta.double(0.0) @@ -134,6 +144,7 @@ private fun SolidGroup.addShape( block = block ) } + "TGeoTubeSeg" -> { val fRmax by shape.meta.double(0.0) val fDz by shape.meta.double(0.0) @@ -151,6 +162,7 @@ private fun SolidGroup.addShape( block = block ) } + "TGeoPcon" -> { val fDphi by shape.meta.double(0.0) val fNz by shape.meta.int(2) @@ -176,6 +188,7 @@ private fun SolidGroup.addShape( TODO() } } + "TGeoPgon" -> { //TODO add a inner polygone layer val fDphi by shape.meta.double(0.0) @@ -206,15 +219,18 @@ private fun SolidGroup.addShape( } }.apply(block) } + "TGeoShapeAssembly" -> { val fVolume by shape.dObject(::DGeoVolume) fVolume?.let { volume -> addRootVolume(volume, context, block = block) } } + "TGeoBBox" -> { box(shape.fDX * 2, shape.fDY * 2, shape.fDZ * 2, name = name, block = block) } + "TGeoTrap" -> { val fTheta by shape.meta.double(0.0) val fPhi by shape.meta.double(0.0) @@ -242,6 +258,7 @@ private fun SolidGroup.addShape( val node8 = Point3D(-fTl2, fH2, fDz) hexagon(node1, node2, node3, node4, node5, node6, node7, node8, name) } + "TGeoScaledShape" -> { val fShape by shape.dObject(::DGeoShape) val fScale by shape.dObject(::DGeoScale) @@ -253,6 +270,7 @@ private fun SolidGroup.addShape( } } } + else -> { TODO("A shape with type ${shape.typename} not implemented") } @@ -267,6 +285,7 @@ private fun SolidGroup.addRootNode(obj: DGeoNode, context: RootToSolidContext) { val fMatrix by obj.dObject(::DGeoMatrix) this.useMatrix(fMatrix) } + "TGeoNodeOffset" -> { val fOffset by obj.meta.double(0.0) x = fOffset @@ -301,10 +320,10 @@ private fun buildVolume(volume: DGeoVolume, context: RootToSolidContext): Solid? } } } - return if (group.isEmpty()) { + return if (group.children.isEmpty()) { null - } else if (group.children.size == 1 && group.meta.isEmpty()) { - (group.children.values.first() as Solid).apply { parent = null } + } else if (group.items.size == 1 && group.meta.isEmpty()) { + group.items.values.first().apply { parent = null } } else { group } @@ -317,7 +336,7 @@ private fun SolidGroup.addRootVolume( context: RootToSolidContext, name: String? = null, cache: Boolean = true, - block: Solid.() -> Unit = {} + block: Solid.() -> Unit = {}, ) { val combinedName = if (volume.fName.isEmpty()) { name @@ -330,7 +349,7 @@ private fun SolidGroup.addRootVolume( if (!cache) { val group = buildVolume(volume, context)?.apply { volume.fFillColor?.let { - meta[MATERIAL_COLOR_KEY] = RootColors[it] + setPropertyValue(MATERIAL_COLOR_KEY, RootColors[it]) } block() } @@ -347,7 +366,7 @@ private fun SolidGroup.addRootVolume( ref(templateName, name).apply { volume.fFillColor?.let { - meta[MATERIAL_COLOR_KEY] = RootColors[it] + setPropertyValue(MATERIAL_COLOR_KEY, RootColors[it]) } block() } diff --git a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/serialization/rootToSolid.kt b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/serialization/rootToSolid.kt index 117e51b3..4483afcb 100644 --- a/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/serialization/rootToSolid.kt +++ b/cern-root-loader/src/commonMain/kotlin/ru/mipt/npm/root/serialization/rootToSolid.kt @@ -160,7 +160,7 @@ private fun SolidGroup.volume(volume: TGeoVolume, name: String? = null, cache: B name = combinedName, obj = group, prototypeHolder = rootPrototypes, - templateName = volumesName + Name.parse(combinedName ?: "volume[${group.hashCode()}]") + prototypeName = volumesName + Name.parse(combinedName ?: "volume[${group.hashCode()}]") ) } diff --git a/demo/gdml/src/commonTest/kotlin/space/kscience/visionforge/gdml/GDMLVisionTest.kt b/demo/gdml/src/commonTest/kotlin/space/kscience/visionforge/gdml/GDMLVisionTest.kt index 12918532..afb23f16 100644 --- a/demo/gdml/src/commonTest/kotlin/space/kscience/visionforge/gdml/GDMLVisionTest.kt +++ b/demo/gdml/src/commonTest/kotlin/space/kscience/visionforge/gdml/GDMLVisionTest.kt @@ -1,13 +1,13 @@ package space.kscience.visionforge.gdml +import space.kscience.dataforge.meta.string import space.kscience.dataforge.names.Name import space.kscience.dataforge.values.asValue -import space.kscience.dataforge.values.string import space.kscience.gdml.GdmlShowCase import space.kscience.visionforge.Vision -import space.kscience.visionforge.computeProperties import space.kscience.visionforge.get -import space.kscience.visionforge.setProperty +import space.kscience.visionforge.getProperty +import space.kscience.visionforge.getPropertyValue import space.kscience.visionforge.solid.Solid import space.kscience.visionforge.solid.SolidMaterial import space.kscience.visionforge.solid.material @@ -20,8 +20,8 @@ class GDMLVisionTest { @Test fun testCubesStyles(){ - val segment = cubes["composite-000.segment-0"] as Solid - println(segment.computeProperties().getValue(Vision.STYLE_KEY)) + val segment = cubes.children["composite-000.segment-0"] as Solid + println(segment.getPropertyValue(Vision.STYLE_KEY)) // println(segment.computePropertyNode(SolidMaterial.MATERIAL_KEY)) // println(segment.computeProperty(SolidMaterial.MATERIAL_COLOR_KEY)) @@ -35,7 +35,7 @@ class GDMLVisionTest { fun testPrototypeProperty() { val child = cubes[Name.of("composite-000","segment-0")] assertNotNull(child) - child.setProperty(SolidMaterial.MATERIAL_COLOR_KEY, "red".asValue()) - assertEquals("red", child.getPropertyValue(SolidMaterial.MATERIAL_COLOR_KEY)?.string) + child.setPropertyValue(SolidMaterial.MATERIAL_COLOR_KEY, "red".asValue()) + assertEquals("red", child.getProperty(SolidMaterial.MATERIAL_COLOR_KEY).string) } } \ No newline at end of file diff --git a/demo/gdml/src/jsMain/kotlin/space/kscience/visionforge/gdml/demo/GDMLAppComponent.kt b/demo/gdml/src/jsMain/kotlin/space/kscience/visionforge/gdml/demo/GDMLAppComponent.kt index c82fa60b..b28dec12 100644 --- a/demo/gdml/src/jsMain/kotlin/space/kscience/visionforge/gdml/demo/GDMLAppComponent.kt +++ b/demo/gdml/src/jsMain/kotlin/space/kscience/visionforge/gdml/demo/GDMLAppComponent.kt @@ -26,7 +26,6 @@ import space.kscience.visionforge.setAsRoot import space.kscience.visionforge.solid.Solid import space.kscience.visionforge.solid.Solids import space.kscience.visionforge.solid.ambientLight -import space.kscience.visionforge.solid.invoke import styled.css import styled.styledDiv diff --git a/demo/gdml/src/jsMain/kotlin/space/kscience/visionforge/gdml/demo/GdmlJsDemoApp.kt b/demo/gdml/src/jsMain/kotlin/space/kscience/visionforge/gdml/demo/GdmlJsDemoApp.kt index edcbe6f0..1cd1b0ff 100644 --- a/demo/gdml/src/jsMain/kotlin/space/kscience/visionforge/gdml/demo/GdmlJsDemoApp.kt +++ b/demo/gdml/src/jsMain/kotlin/space/kscience/visionforge/gdml/demo/GdmlJsDemoApp.kt @@ -10,7 +10,6 @@ import space.kscience.visionforge.Colors import space.kscience.visionforge.gdml.toVision import space.kscience.visionforge.react.render import space.kscience.visionforge.solid.ambientLight -import space.kscience.visionforge.solid.invoke import space.kscience.visionforge.solid.three.ThreePlugin import space.kscience.visionforge.startApplication import styled.injectGlobal diff --git a/demo/js-playground/src/main/kotlin/JsPlaygroundApp.kt b/demo/js-playground/src/main/kotlin/JsPlaygroundApp.kt index 1cd2611b..c31b48e0 100644 --- a/demo/js-playground/src/main/kotlin/JsPlaygroundApp.kt +++ b/demo/js-playground/src/main/kotlin/JsPlaygroundApp.kt @@ -75,7 +75,7 @@ private class JsPlaygroundApp : Application { context = playgroundContext solid { ambientLight { - color(Colors.white) + color.set(Colors.white) } repeat(100) { sphere(5, name = "sphere[$it]") { @@ -83,7 +83,7 @@ private class JsPlaygroundApp : Application { y = random.nextDouble(-300.0, 300.0) z = random.nextDouble(-300.0, 300.0) material { - color(random.nextInt()) + color.set(random.nextInt()) } detail = 16 } diff --git a/demo/js-playground/src/main/kotlin/gravityDemo.kt b/demo/js-playground/src/main/kotlin/gravityDemo.kt index 4d420cc7..3bdd574c 100644 --- a/demo/js-playground/src/main/kotlin/gravityDemo.kt +++ b/demo/js-playground/src/main/kotlin/gravityDemo.kt @@ -43,13 +43,13 @@ val GravityDemo = fc { props -> context = props.context solid { pointLight(200, 200, 200, name = "light"){ - color(Colors.white) + color.set(Colors.white) } ambientLight() sphere(5.0, "ball") { detail = 16 - color("red") + color.set("red") val h = 100.0 y = h context.launch { diff --git a/demo/muon-monitor/src/commonMain/kotlin/ru/mipt/npm/muon/monitor/Model.kt b/demo/muon-monitor/src/commonMain/kotlin/ru/mipt/npm/muon/monitor/Model.kt index 06501374..596b11dd 100644 --- a/demo/muon-monitor/src/commonMain/kotlin/ru/mipt/npm/muon/monitor/Model.kt +++ b/demo/muon-monitor/src/commonMain/kotlin/ru/mipt/npm/muon/monitor/Model.kt @@ -3,24 +3,18 @@ package ru.mipt.npm.muon.monitor import ru.mipt.npm.muon.monitor.Monitor.CENTRAL_LAYER_Z import ru.mipt.npm.muon.monitor.Monitor.LOWER_LAYER_Z import ru.mipt.npm.muon.monitor.Monitor.UPPER_LAYER_Z +import space.kscience.visionforge.VisionContainerBuilder import space.kscience.visionforge.VisionManager -import space.kscience.visionforge.removeAll import space.kscience.visionforge.setAsRoot -import space.kscience.visionforge.setProperty import space.kscience.visionforge.solid.* -import kotlin.collections.HashMap -import kotlin.collections.HashSet -import kotlin.collections.filter -import kotlin.collections.forEach import kotlin.collections.set -import kotlin.collections.toTypedArray import kotlin.math.PI class Model(val manager: VisionManager) { private val map = HashMap() private val events = HashSet() - private fun SolidGroup.pixel(pixel: SC1) { + private fun VisionContainerBuilder.pixel(pixel: SC1) { val group = group(pixel.name) { position = Point3D(pixel.center.x, pixel.center.y, pixel.center.z) box(pixel.xSize, pixel.ySize, pixel.zSize) @@ -45,7 +39,7 @@ class Model(val manager: VisionManager) { val root: SolidGroup = SolidGroup().apply { setAsRoot(this@Model.manager) material { - color("darkgreen") + color.set("darkgreen") } rotationX = PI / 2 group("bottom") { @@ -70,14 +64,14 @@ class Model(val manager: VisionManager) { private fun highlight(pixel: String) { println("highlight $pixel") - map[pixel]?.color?.invoke("blue") + map[pixel]?.color.set("blue") } fun reset() { map.values.forEach { - it.setProperty(SolidMaterial.MATERIAL_COLOR_KEY, null) + it.setPropertyValue(SolidMaterial.MATERIAL_COLOR_KEY, null) } - tracks.removeAll() + tracks.children.clear() } fun displayEvent(event: Event) { @@ -89,7 +83,7 @@ class Model(val manager: VisionManager) { event.track?.let { tracks.polyline(*it.toTypedArray(), name = "track[${event.id}]") { thickness = 4 - color("red") + color.set("red") } } } diff --git a/demo/muon-monitor/src/jsMain/kotlin/ru/mipt/npm/muon/monitor/MMAppComponent.kt b/demo/muon-monitor/src/jsMain/kotlin/ru/mipt/npm/muon/monitor/MMAppComponent.kt index f9c03756..77ad5845 100644 --- a/demo/muon-monitor/src/jsMain/kotlin/ru/mipt/npm/muon/monitor/MMAppComponent.kt +++ b/demo/muon-monitor/src/jsMain/kotlin/ru/mipt/npm/muon/monitor/MMAppComponent.kt @@ -25,7 +25,6 @@ import space.kscience.visionforge.react.flexRow import space.kscience.visionforge.ring.ThreeCanvasWithControls import space.kscience.visionforge.ring.tab import space.kscience.visionforge.solid.ambientLight -import space.kscience.visionforge.solid.invoke import space.kscience.visionforge.solid.specifications.Canvas3DOptions import space.kscience.visionforge.solid.three.edges import styled.css diff --git a/demo/playground/src/jvmMain/kotlin/gdmlCurve.kt b/demo/playground/src/jvmMain/kotlin/gdmlCurve.kt index 70827a2d..f98a0b81 100644 --- a/demo/playground/src/jvmMain/kotlin/gdmlCurve.kt +++ b/demo/playground/src/jvmMain/kotlin/gdmlCurve.kt @@ -7,7 +7,7 @@ import space.kscience.visionforge.gdml.toVision import space.kscience.visionforge.html.ResourceLocation import space.kscience.visionforge.solid.Solids import space.kscience.visionforge.solid.color -import space.kscience.visionforge.solid.invoke +import space.kscience.visionforge.solid.set import space.kscience.visionforge.visible import java.nio.file.Path @@ -229,7 +229,7 @@ fun main() = makeVisionFile(Path.of("curves.html"), resourceLocation = ResourceL visible = false } if(solid.name.startsWith("gas")){ - color("green") + color.set("green") } else { //make all solids semi-transparent transparent() diff --git a/demo/playground/src/jvmMain/kotlin/gdmlIaxo.kt b/demo/playground/src/jvmMain/kotlin/gdmlIaxo.kt index 24c56304..ee47c461 100644 --- a/demo/playground/src/jvmMain/kotlin/gdmlIaxo.kt +++ b/demo/playground/src/jvmMain/kotlin/gdmlIaxo.kt @@ -5,7 +5,7 @@ import space.kscience.visionforge.Colors import space.kscience.visionforge.gdml.gdml import space.kscience.visionforge.solid.Solids import space.kscience.visionforge.solid.ambientLight -import space.kscience.visionforge.solid.invoke +import space.kscience.visionforge.solid.set import space.kscience.visionforge.solid.solid fun main() = makeVisionFile { @@ -13,7 +13,7 @@ fun main() = makeVisionFile { requirePlugin(Solids) solid { ambientLight { - color(Colors.white) + color.set(Colors.white) } gdml(GdmlShowCase.babyIaxo(), "D0") } diff --git a/demo/playground/src/jvmMain/kotlin/randomSpheres.kt b/demo/playground/src/jvmMain/kotlin/randomSpheres.kt index fd1b9865..47201ff8 100644 --- a/demo/playground/src/jvmMain/kotlin/randomSpheres.kt +++ b/demo/playground/src/jvmMain/kotlin/randomSpheres.kt @@ -19,7 +19,7 @@ fun main() = makeVisionFile( vision { solid { ambientLight { - color(Colors.white) + color.set(Colors.white) } repeat(100) { sphere(5, name = "sphere[$it]") { @@ -27,7 +27,7 @@ fun main() = makeVisionFile( y = random.nextDouble(-300.0, 300.0) z = random.nextDouble(-300.0, 300.0) material { - color(random.nextInt()) + color.set(random.nextInt()) } detail = 16 } diff --git a/demo/playground/src/jvmMain/kotlin/simpleCube.kt b/demo/playground/src/jvmMain/kotlin/simpleCube.kt index e1fc91eb..5dae515c 100644 --- a/demo/playground/src/jvmMain/kotlin/simpleCube.kt +++ b/demo/playground/src/jvmMain/kotlin/simpleCube.kt @@ -2,8 +2,8 @@ package space.kscience.visionforge.examples import space.kscience.visionforge.html.ResourceLocation import space.kscience.visionforge.solid.box -import space.kscience.visionforge.solid.invoke import space.kscience.visionforge.solid.material +import space.kscience.visionforge.solid.set import space.kscience.visionforge.solid.solid fun main() = makeVisionFile(resourceLocation = ResourceLocation.SYSTEM) { @@ -11,7 +11,7 @@ fun main() = makeVisionFile(resourceLocation = ResourceLocation.SYSTEM) { solid { box(100, 100, 100) material { - emissiveColor("red") + emissiveColor.set("red") } } } diff --git a/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/geometry.kt b/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/geometry.kt index 93650fb2..631d70f8 100644 --- a/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/geometry.kt +++ b/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/geometry.kt @@ -15,7 +15,7 @@ internal fun visionOfSatellite( ySegmentSize: Number = xSegmentSize, fiberDiameter: Number = 1.0, ): SolidGroup = SolidGroup { - color("darkgreen") + color.set("darkgreen") val transparent by style { this[SolidMaterial.MATERIAL_OPACITY_KEY] = 0.3 } diff --git a/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/satServer.kt b/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/satServer.kt index fbacc5b1..d601a74b 100644 --- a/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/satServer.kt +++ b/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/satServer.kt @@ -44,7 +44,7 @@ fun main() { val randomJ = Random.nextInt(1, 4) val target = Name.parse("layer[$randomLayer].segment[$randomI,$randomJ]") val targetVision = sat[target] as Solid - targetVision.color("red") + targetVision.color.set("red") delay(1000) targetVision.color.clear() delay(500) diff --git a/demo/solid-showcase/src/commonMain/kotlin/space/kscience/visionforge/solid/demo/demo.kt b/demo/solid-showcase/src/commonMain/kotlin/space/kscience/visionforge/solid/demo/demo.kt index 5631de56..228f63f7 100644 --- a/demo/solid-showcase/src/commonMain/kotlin/space/kscience/visionforge/solid/demo/demo.kt +++ b/demo/solid-showcase/src/commonMain/kotlin/space/kscience/visionforge/solid/demo/demo.kt @@ -20,7 +20,7 @@ fun VisionLayout.demo(name: String, title: String = name, block: SolidGro } val vision = SolidGroup(block).apply { ambientLight{ - color(Colors.white) + color.set(Colors.white) } } render(Name.parse(name), vision, meta) @@ -46,23 +46,23 @@ fun VisionLayout.showcase() { ambientLight() box(100.0, 100.0, 100.0) { z = -110.0 - color("teal") + color.set("teal") } sphere(50.0) { x = 110 detail = 16 - color("red") + color.set("red") } tube(50, height = 10, innerRadius = 25, angle = PI) { y = 110 detail = 16 rotationX = PI / 4 - color("blue") + color.set("blue") } sphereLayer(50, 40, theta = PI / 2) { rotationX = -PI * 3 / 4 z = 110 - color(Colors.pink) + color.set(Colors.pink) } } @@ -77,7 +77,7 @@ fun VisionLayout.showcase() { visible = false x = 110.0 //override color for this cube - color(1530) + color.set(1530) GlobalScope.launch(Dispatchers.Main) { while (isActive) { @@ -92,7 +92,7 @@ fun VisionLayout.showcase() { val random = Random(111) while (isActive) { delay(1000) - group.color(random.nextInt(0, Int.MAX_VALUE)) + group.color.set(random.nextInt(0, Int.MAX_VALUE)) } } } @@ -104,7 +104,7 @@ fun VisionLayout.showcase() { rotationY = PI / 4 box(100, 100, 100) { rotationZ = PI / 4 - color(Colors.red) + color.set(Colors.red) } } } @@ -117,7 +117,7 @@ fun VisionLayout.showcase() { for (i in 0..100) { layer(i * 5, 20 * sin(2 * PI / 100 * i), 20 * cos(2 * PI / 100 * i)) } - color(Colors.teal) + color.set(Colors.teal) rotationX = -PI / 2 } } @@ -126,13 +126,13 @@ fun VisionLayout.showcase() { sphere(100) { detail = 32 opacity = 0.4 - color(Colors.blue) + color.set(Colors.blue) } repeat(20) { polyline(Point3D(100, 100, 100), Point3D(-100, -100, -100)) { thickness = 3.0 rotationX = it * PI2 / 20 - color(Colors.green) + color.set(Colors.green) //rotationY = it * PI2 / 20 } } @@ -159,7 +159,7 @@ fun VisionLayout.showcaseCSG() { detail = 32 } material { - color(Colors.pink) + color.set(Colors.pink) } } composite(CompositeType.UNION) { @@ -169,7 +169,7 @@ fun VisionLayout.showcaseCSG() { sphere(50) { detail = 32 } - color("lightgreen") + color.set("lightgreen") opacity = 0.7 } composite(CompositeType.SUBTRACT) { @@ -180,7 +180,7 @@ fun VisionLayout.showcaseCSG() { sphere(50) { detail = 32 } - color("teal") + color.set("teal") opacity = 0.7 } } @@ -191,7 +191,7 @@ fun VisionLayout.showcaseCSG() { detail = 32 } box(100, 100, 100) - color("red") + color.set("red") opacity = 0.5 } } diff --git a/demo/solid-showcase/src/jsMain/kotlin/space/kscience/visionforge/solid/demo/VariableBox.kt b/demo/solid-showcase/src/jsMain/kotlin/space/kscience/visionforge/solid/demo/VariableBox.kt index af828f46..84a164c8 100644 --- a/demo/solid-showcase/src/jsMain/kotlin/space/kscience/visionforge/solid/demo/VariableBox.kt +++ b/demo/solid-showcase/src/jsMain/kotlin/space/kscience/visionforge/solid/demo/VariableBox.kt @@ -11,7 +11,6 @@ import space.kscience.dataforge.names.startsWith import space.kscience.dataforge.values.asValue import space.kscience.visionforge.onPropertyChange import space.kscience.visionforge.set -import space.kscience.visionforge.setProperty import space.kscience.visionforge.solid.SolidGroup import space.kscience.visionforge.solid.layer import space.kscience.visionforge.solid.three.* @@ -72,7 +71,7 @@ internal class VariableBox(val xSize: Number, val ySize: Number) : ThreeJsVision var value: Int get() = meta[VALUE].int ?: 0 set(value) { - setProperty(VALUE, value.asValue()) + setPropertyValue(VALUE, value.asValue()) } companion object { diff --git a/docs/hierarchy.md b/docs/hierarchy.md index 720af5d2..15f3306e 100644 --- a/docs/hierarchy.md +++ b/docs/hierarchy.md @@ -3,7 +3,7 @@ ![](../docs/images/hierarchy.png) ### Vision -* function `getPropertyValue(name: Name, inherit: Boolean = false, includeStyles: Boolean = true, includeDefaults: Boolean = true)` - get property value with given layer flags. +* function `getProperty(name: Name, inherit: Boolean = false, includeStyles: Boolean = true, includeDefaults: Boolean = true)` - get property value with given layer flags. * function `setProperty(name: Name, item: Any?)` - a convenient method to set property node or value. If `item` is null, then node is removed, not a value Sets the `item` property to the element with the `name` identification. diff --git a/docs/inheritance.md b/docs/inheritance.md index d99451f3..1fad27d2 100644 --- a/docs/inheritance.md +++ b/docs/inheritance.md @@ -7,7 +7,7 @@ Properties, which can be inherited by objects, are `styles`, `prototypes` (if th All values of `styles` property are contained in class `StyleSheet`, where they all are defined at `Group`s level. The `prototypes` property tree is defined in `SolidGroup` class via `PrototypeHolder` interface, and `SolidReference` class helps to reuse a template object. -The order of inheritance of properties is set in function `getPropertyValue` in `VisionBase` class. +The order of inheritance of properties is set in function `getProperty` in `VisionBase` class. The order is this: * own styles * prototypes diff --git a/docs/uml/Vision.puml b/docs/uml/Vision.puml index 04e873b9..f590a13e 100644 --- a/docs/uml/Vision.puml +++ b/docs/uml/Vision.puml @@ -3,7 +3,7 @@ interface Vision{ val parent: VisionGroup? - fun getPropertyValue(name,inherit,includeStyles,includeDefaults): Value? + fun getProperty(name,inherit,includeStyles,includeDefaults): Value? } interface Solid{ @@ -81,7 +81,7 @@ Solid <--- Composite interface SolidReference{ val prototype: Solid - fun getPropertyValue(name,inherit,includeStyles,includeDefaults): Value? + fun getProperty(name,inherit,includeStyles,includeDefaults): Value? } VisionGroup <---- SolidReference SolidReferenceGroup -- SolidReference @@ -91,7 +91,7 @@ class SolidReferenceGroup{ var properties: MutableMeta? val prototype: Solid val children: Map - fun getPropertyValue(name,inherit,includeStyles,includeDefaults): Value? + fun getProperty(name,inherit,includeStyles,includeDefaults): Value? } VisionBase <-- SolidReferenceGroup VisionGroup <-- SolidReferenceGroup diff --git a/gradle.properties b/gradle.properties index 95d6d923..92f33679 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,4 +6,4 @@ kotlin.jupyter.add.scanner=false org.gradle.parallel=true org.gradle.jvmargs=-Xmx4G -toolsVersion=0.11.7-kotlin-1.7.0 \ No newline at end of file +toolsVersion=0.11.8-kotlin-1.7.10 \ No newline at end of file diff --git a/ui/bootstrap/src/main/kotlin/space/kscience/visionforge/bootstrap/threeControls.kt b/ui/bootstrap/src/main/kotlin/space/kscience/visionforge/bootstrap/threeControls.kt index e7602b0e..9ab0403c 100644 --- a/ui/bootstrap/src/main/kotlin/space/kscience/visionforge/bootstrap/threeControls.kt +++ b/ui/bootstrap/src/main/kotlin/space/kscience/visionforge/bootstrap/threeControls.kt @@ -10,8 +10,8 @@ import react.fc import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.isEmpty import space.kscience.visionforge.Vision -import space.kscience.visionforge.VisionGroup import space.kscience.visionforge.react.visionTree +import space.kscience.visionforge.solid.SolidGroup import space.kscience.visionforge.solid.specifications.Canvas3DOptions import styled.css import styled.styledDiv @@ -51,7 +51,7 @@ public val ThreeControls: FC = fc { props -> val selectedObject: Vision? = when { selected == null -> null selected.isEmpty() -> props.vision - else -> (props.vision as? VisionGroup)?.get(selected) + else -> (props.vision as? SolidGroup)?.get(selected) } if (selectedObject != null) { visionPropertyEditor(selectedObject, key = selected) diff --git a/ui/bootstrap/src/main/kotlin/space/kscience/visionforge/bootstrap/visionPropertyEditor.kt b/ui/bootstrap/src/main/kotlin/space/kscience/visionforge/bootstrap/visionPropertyEditor.kt index a65c1f42..4393565a 100644 --- a/ui/bootstrap/src/main/kotlin/space/kscience/visionforge/bootstrap/visionPropertyEditor.kt +++ b/ui/bootstrap/src/main/kotlin/space/kscience/visionforge/bootstrap/visionPropertyEditor.kt @@ -2,13 +2,14 @@ package space.kscience.visionforge.bootstrap import org.w3c.dom.Element import react.RBuilder -import react.dom.render +import react.dom.client.createRoot import space.kscience.dataforge.meta.descriptors.MetaDescriptor import space.kscience.visionforge.Vision import space.kscience.visionforge.computeProperties import space.kscience.visionforge.getStyle import space.kscience.visionforge.react.metaViewer import space.kscience.visionforge.react.propertyEditor +import space.kscience.visionforge.react.render import space.kscience.visionforge.solid.SolidReference import space.kscience.visionforge.styles @@ -50,6 +51,6 @@ public fun RBuilder.visionPropertyEditor( public fun Element.visionPropertyEditor( item: Vision, descriptor: MetaDescriptor? = item.descriptor, -): Unit = render(this) { +): Unit = createRoot(this).render { visionPropertyEditor(item, descriptor = descriptor) } \ No newline at end of file diff --git a/ui/react/src/main/kotlin/space/kscience/visionforge/react/VisionTree.kt b/ui/react/src/main/kotlin/space/kscience/visionforge/react/VisionTree.kt index 54fc8ec4..84f9347f 100644 --- a/ui/react/src/main/kotlin/space/kscience/visionforge/react/VisionTree.kt +++ b/ui/react/src/main/kotlin/space/kscience/visionforge/react/VisionTree.kt @@ -59,9 +59,9 @@ private fun RBuilder.visionTree(props: ObjectTreeProps): Unit { val obj = props.obj //display as node if any child is visible - if (obj is VisionGroup) { + if (obj is VisionGroup<*>) { flexRow { - if (obj.children.any { !it.key.body.startsWith("@") }) { + if (obj.items.any { !it.key.body.startsWith("@") }) { styledSpan { css { +TreeStyles.treeCaret @@ -81,9 +81,9 @@ private fun RBuilder.visionTree(props: ObjectTreeProps): Unit { css { +TreeStyles.tree } - obj.children.entries + obj.items.entries .filter { !it.key.toString().startsWith("@") } // ignore statics and other hidden children - .sortedBy { (it.value as? VisionGroup)?.isEmpty() ?: true } // ignore empty groups + .sortedBy { (it.value as? VisionGroup<*>)?.isEmpty() ?: true } // ignore empty groups .forEach { (childToken, child) -> styledDiv { css { @@ -92,7 +92,7 @@ private fun RBuilder.visionTree(props: ObjectTreeProps): Unit { child(ObjectTree) { attrs { this.name = props.name + childToken - this.obj = child + this.obj = child.vision this.selected = props.selected this.clickCallback = props.clickCallback } diff --git a/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ThreeViewWithControls.kt b/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ThreeViewWithControls.kt index fa3fab99..085ff608 100644 --- a/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ThreeViewWithControls.kt +++ b/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ThreeViewWithControls.kt @@ -108,7 +108,7 @@ public val ThreeCanvasWithControls: FC = fc("Three selected?.let { when { it.isEmpty() -> solid - else -> (solid as? VisionGroup)?.get(it) + else -> (solid as? SolidGroup)?.get(it) } } } @@ -165,7 +165,7 @@ public val ThreeCanvasWithControls: FC = fc("Three } IslandContent { propertyEditor( - ownProperties = vision.meta, + ownProperties = vision.properties(), allProperties = vision.computeProperties(), descriptor = vision.descriptor, key = selected diff --git a/visionforge-core/api/visionforge-core.api b/visionforge-core/api/visionforge-core.api index 0937e26d..f9099a99 100644 --- a/visionforge-core/api/visionforge-core.api +++ b/visionforge-core/api/visionforge-core.api @@ -183,7 +183,7 @@ public class space/kscience/visionforge/SimpleVisionPropertyContainer : space/ks public fun (Lspace/kscience/dataforge/meta/ObservableMutableMeta;)V public synthetic fun getMeta ()Lspace/kscience/dataforge/meta/MutableMeta; public fun getMeta ()Lspace/kscience/dataforge/meta/ObservableMutableMeta; - public fun getPropertyValue (Lspace/kscience/dataforge/names/Name;ZZZ)Lspace/kscience/dataforge/values/Value; + public fun getProperty (Lspace/kscience/dataforge/names/Name;ZZZ)Lspace/kscience/dataforge/values/Value; } public final class space/kscience/visionforge/StyleReference { @@ -241,8 +241,8 @@ public abstract interface class space/kscience/visionforge/Vision : space/kscien public fun getManager ()Lspace/kscience/visionforge/VisionManager; public abstract fun getMeta ()Lspace/kscience/dataforge/meta/ObservableMutableMeta; public abstract fun getParent ()Lspace/kscience/visionforge/VisionGroup; - public abstract fun getPropertyValue (Lspace/kscience/dataforge/names/Name;ZZZ)Lspace/kscience/dataforge/values/Value; - public static synthetic fun getPropertyValue$default (Lspace/kscience/visionforge/Vision;Lspace/kscience/dataforge/names/Name;ZZZILjava/lang/Object;)Lspace/kscience/dataforge/values/Value; + public abstract fun getProperty (Lspace/kscience/dataforge/names/Name;ZZZ)Lspace/kscience/dataforge/values/Value; + public static synthetic fun getProperty$default (Lspace/kscience/visionforge/Vision;Lspace/kscience/dataforge/names/Name;ZZZILjava/lang/Object;)Lspace/kscience/dataforge/values/Value; public abstract fun invalidateProperty (Lspace/kscience/dataforge/names/Name;)V public abstract fun setParent (Lspace/kscience/visionforge/VisionGroup;)V public abstract fun update (Lspace/kscience/visionforge/VisionChange;)V @@ -266,7 +266,7 @@ public class space/kscience/visionforge/VisionBase : space/kscience/visionforge/ protected final fun getOrCreateProperties ()Lspace/kscience/dataforge/meta/MutableMeta; public fun getParent ()Lspace/kscience/visionforge/VisionGroup; protected final fun getProperties ()Lspace/kscience/dataforge/meta/MutableMeta; - public fun getPropertyValue (Lspace/kscience/dataforge/names/Name;ZZZ)Lspace/kscience/dataforge/values/Value; + public fun getProperty (Lspace/kscience/dataforge/names/Name;ZZZ)Lspace/kscience/dataforge/values/Value; public fun invalidateProperty (Lspace/kscience/dataforge/names/Name;)V public fun setParent (Lspace/kscience/visionforge/VisionGroup;)V protected final fun setProperties (Lspace/kscience/dataforge/meta/MutableMeta;)V @@ -446,8 +446,8 @@ public final class space/kscience/visionforge/VisionGroupKt { public final class space/kscience/visionforge/VisionKt { public static final fun getPropertyChanges (Lspace/kscience/visionforge/Vision;)Lkotlinx/coroutines/flow/Flow; - public static final fun getPropertyValue (Lspace/kscience/visionforge/Vision;Ljava/lang/String;ZZZ)Lspace/kscience/dataforge/values/Value; - public static synthetic fun getPropertyValue$default (Lspace/kscience/visionforge/Vision;Ljava/lang/String;ZZZILjava/lang/Object;)Lspace/kscience/dataforge/values/Value; + public static final fun getProperty (Lspace/kscience/visionforge/Vision;Ljava/lang/String;ZZZ)Lspace/kscience/dataforge/values/Value; + public static synthetic fun getProperty$default (Lspace/kscience/visionforge/Vision;Ljava/lang/String;ZZZILjava/lang/Object;)Lspace/kscience/dataforge/values/Value; public static final fun getVisible (Lspace/kscience/visionforge/Vision;)Ljava/lang/Boolean; public static final fun onPropertyChange (Lspace/kscience/visionforge/Vision;Lkotlin/jvm/functions/Function2;)V public static final fun setProperty (Lspace/kscience/visionforge/Vision;Lspace/kscience/dataforge/names/Name;Ljava/lang/Object;)V @@ -499,8 +499,8 @@ public abstract class space/kscience/visionforge/VisionPlugin : space/kscience/d public abstract interface class space/kscience/visionforge/VisionPropertyContainer { public abstract fun getMeta ()Lspace/kscience/dataforge/meta/MutableMeta; - public abstract fun getPropertyValue (Lspace/kscience/dataforge/names/Name;ZZZ)Lspace/kscience/dataforge/values/Value; - public static synthetic fun getPropertyValue$default (Lspace/kscience/visionforge/VisionPropertyContainer;Lspace/kscience/dataforge/names/Name;ZZZILjava/lang/Object;)Lspace/kscience/dataforge/values/Value; + public abstract fun getProperty (Lspace/kscience/dataforge/names/Name;ZZZ)Lspace/kscience/dataforge/values/Value; + public static synthetic fun getProperty$default (Lspace/kscience/visionforge/VisionPropertyContainer;Lspace/kscience/dataforge/names/Name;ZZZILjava/lang/Object;)Lspace/kscience/dataforge/values/Value; } public final class space/kscience/visionforge/html/HeadersKt { diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/AbstractVision.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/AbstractVision.kt new file mode 100644 index 00000000..c6cba1ba --- /dev/null +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/AbstractVision.kt @@ -0,0 +1,107 @@ +package space.kscience.visionforge + +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.launch +import kotlinx.serialization.Serializable +import kotlinx.serialization.Transient +import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.MutableMeta +import space.kscience.dataforge.meta.asMutableMeta +import space.kscience.dataforge.meta.descriptors.MetaDescriptor +import space.kscience.dataforge.meta.get +import space.kscience.dataforge.names.Name +import space.kscience.dataforge.names.asName +import space.kscience.dataforge.names.isEmpty +import space.kscience.dataforge.values.Value +import space.kscience.visionforge.VisionGroup.Companion.updateProperties +import kotlin.jvm.Synchronized + +@Serializable +public abstract class AbstractVision : Vision { + + @Transient + override var parent: Vision? = null + + protected var properties: MutableMeta? = null + + override val meta: Meta get() = properties ?: Meta.EMPTY + + @Synchronized + private fun getOrCreateProperties(): MutableMeta { + if (properties == null) { + //TODO check performance issues + val newProperties = MutableMeta() + properties = newProperties + } + return properties!! + } + + @Transient + private val _propertyChanges = MutableSharedFlow() + override val propertyChanges: SharedFlow get() = _propertyChanges + + override fun getPropertyValue( + name: Name, + inherit: Boolean, + includeStyles: Boolean, + includeDefaults: Boolean, + ): Value? { + properties?.get(name)?.value?.let { return it } + if (includeStyles) { + getStyleProperty(name)?.value?.let { return it } + } + if (inherit) { + parent?.getPropertyValue(name, inherit, includeStyles, includeDefaults)?.let { return it } + } + if (includeDefaults) { + descriptor?.defaultNode?.get(name)?.value?.let { return it } + } + return null + } + + override fun setProperty(name: Name, node: Meta?) { + //TODO check old value? + if (name.isEmpty()) { + properties = node?.asMutableMeta() + } else if (node == null) { + properties?.setMeta(name, node) + } else { + getOrCreateProperties().setMeta(name, node) + } + invalidateProperty(name) + } + + override fun setPropertyValue(name: Name, value: Value?) { + //TODO check old value? + if (value == null) { + properties?.getMeta(name)?.value = null + } else { + getOrCreateProperties().setValue(name, value) + } + invalidateProperty(name) + } + + override val descriptor: MetaDescriptor? get() = null + + override fun invalidateProperty(propertyName: Name) { + if (propertyName == Vision.STYLE_KEY) { + styles.asSequence() + .mapNotNull { getStyle(it) } + .flatMap { it.items.asSequence() } + .distinctBy { it.key } + .forEach { + invalidateProperty(it.key.asName()) + } + } + manager.context.launch { + _propertyChanges.emit(propertyName) + } + } + + override fun update(change: VisionChange) { + change.properties?.let { + updateProperties(it, Name.EMPTY) + } + } +} \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/ComputedVisionProperties.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/ComputedVisionProperties.kt deleted file mode 100644 index 925eecfe..00000000 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/ComputedVisionProperties.kt +++ /dev/null @@ -1,84 +0,0 @@ -package space.kscience.visionforge - -import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.meta.descriptors.MetaDescriptor -import space.kscience.dataforge.meta.descriptors.get -import space.kscience.dataforge.meta.get -import space.kscience.dataforge.names.Name -import space.kscience.dataforge.names.NameToken -import space.kscience.dataforge.names.plus -import space.kscience.dataforge.values.MutableValueProvider -import space.kscience.dataforge.values.Value - -private class ComputedVisionProperties( - val vision: Vision, - val pathName: Name, - val visionDescriptor: MetaDescriptor, - val parentInheritFlag: Boolean?, - val parentStylesFlag: Boolean? -) : Meta { - - val descriptor: MetaDescriptor? by lazy { visionDescriptor[pathName] } - - override val items: Map - get() { - val metaKeys = vision.meta.getMeta(pathName)?.items?.keys ?: emptySet() - val descriptorKeys = descriptor?.children?.map { NameToken(it.key) } ?: emptySet() - val inheritFlag = descriptor?.inherited ?: parentInheritFlag - val stylesFlag = descriptor?.usesStyles ?: parentStylesFlag - return (metaKeys + descriptorKeys).associateWith { - ComputedVisionProperties( - vision, - pathName + it, - visionDescriptor, - inheritFlag, - stylesFlag - ) - } - } - - override val value: Value? - get() { - val inheritFlag = descriptor?.inherited ?: parentInheritFlag ?: false - val stylesFlag = descriptor?.usesStyles ?: parentStylesFlag ?: true - return vision.getPropertyValue(pathName, inheritFlag, stylesFlag, true) - } - - override fun toString(): String = Meta.toString(this) - override fun equals(other: Any?): Boolean = Meta.equals(this, other as? Meta) - override fun hashCode(): Int = Meta.hashCode(this) -} - -/** - * Compute property node based on inheritance and style information from the descriptor - */ -public fun Vision.computeProperties(descriptor: MetaDescriptor? = this.descriptor): Meta = - if (descriptor == null) meta else ComputedVisionProperties(this, Name.EMPTY, descriptor, null, null) - -public fun Vision.computePropertyNode( - name: Name, - descriptor: MetaDescriptor? = this.descriptor -): Meta? = computeProperties(descriptor)[name] - -/** - * Compute the property based on the provided value descriptor. By default, use Vision own descriptor - */ -public fun Vision.computeProperty(name: Name, valueDescriptor: MetaDescriptor? = descriptor?.get(name)): Value? { - val inheritFlag = valueDescriptor?.inherited ?: false - val stylesFlag = valueDescriptor?.usesStyles ?: true - return getPropertyValue(name, inheritFlag, stylesFlag) -} - -/** - * Accessor to all vision properties - */ -public fun Vision.computePropertyValues( - descriptor: MetaDescriptor? = this.descriptor -): MutableValueProvider = object : MutableValueProvider { - override fun getValue(name: Name): Value? = computeProperty(name, descriptor?.get(name)) - - override fun setValue(name: Name, value: Value?) { - setProperty(name, value) - } -} - diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/StyleReference.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/StyleReference.kt index d5dfac6e..0e2b6132 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/StyleReference.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/StyleReference.kt @@ -9,7 +9,7 @@ import kotlin.properties.ReadOnlyProperty /** * A reference to a style defined in a specific container */ -public class StyleReference(public val owner: VisionGroup, public val name: String) +public class StyleReference(public val owner: Vision, public val name: String) private tailrec fun styleIsDefined(vision: Vision, reference: StyleReference): Boolean = when { reference.owner === vision -> true @@ -25,7 +25,7 @@ public fun Vision.useStyle(reference: StyleReference) { } @VisionBuilder -public fun VisionGroup.style( +public fun Vision.style( styleKey: String? = null, builder: MutableMeta.() -> Unit, ): ReadOnlyProperty = ReadOnlyProperty { _, property -> @@ -35,7 +35,7 @@ public fun VisionGroup.style( } @VisionBuilder -public fun VisionGroup.style( +public fun Vision.style( spec: Specification, styleKey: String? = null, builder: T.() -> Unit, diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/StyleSheet.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/StyleSheet.kt index 52deee3c..cc465d87 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/StyleSheet.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/StyleSheet.kt @@ -5,7 +5,6 @@ import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.NameToken import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.plus -import space.kscience.dataforge.values.Value import space.kscience.dataforge.values.asValue import space.kscience.dataforge.values.stringList import kotlin.jvm.JvmInline @@ -14,11 +13,11 @@ import kotlin.jvm.JvmInline * A container for styles */ @JvmInline -public value class StyleSheet(private val owner: VisionGroup) { +public value class StyleSheet(private val owner: Vision) { - private val styleNode: Meta? get() = owner.meta[STYLESHEET_KEY] + private val styleNode: Meta get() = owner.getProperty(STYLESHEET_KEY) - public val items: Map? get() = styleNode?.items + public val items: Map get() = styleNode.items public operator fun get(key: String): Meta? = owner.getStyle(key) @@ -26,7 +25,7 @@ public value class StyleSheet(private val owner: VisionGroup) { * Define a style without notifying owner */ public fun define(key: String, style: Meta?) { - owner.meta.setMeta(STYLESHEET_KEY + key, style) + owner.setProperty(STYLESHEET_KEY + key, style) } /** @@ -43,7 +42,7 @@ public value class StyleSheet(private val owner: VisionGroup) { /** * Create and set a style */ - public operator fun set(key: String, builder: MutableMeta.() -> Unit) { + public fun update(key: String, builder: MutableMeta.() -> Unit) { val newStyle = get(key)?.toMutableMeta()?.apply(builder) ?: Meta(builder) set(key, newStyle.seal()) } @@ -61,10 +60,8 @@ internal fun Vision.styleChanged(key: String, oldStyle: Meta?, newStyle: Meta?) .map { it.asName() } tokens.forEach { parent?.invalidateProperty(it) } } - if (this is VisionGroup) { - for (obj in this) { - obj.styleChanged(key, oldStyle, newStyle) - } + children.values.forEach { vision -> + vision.styleChanged(key, oldStyle, newStyle) } } @@ -73,35 +70,40 @@ internal fun Vision.styleChanged(key: String, oldStyle: Meta?, newStyle: Meta?) * List of names of styles applied to this object. Order matters. Not inherited. */ public var Vision.styles: List - get() = meta.getValue(Vision.STYLE_KEY)?.stringList ?: emptyList() + get() = getPropertyValue( + Vision.STYLE_KEY, + inherit = true, + includeStyles = false, + includeDefaults = false + )?.stringList ?: emptyList() set(value) { - meta.setValue(Vision.STYLE_KEY, value.map { it.asValue() }.asValue()) + setPropertyValue(Vision.STYLE_KEY, value.map { it.asValue() }.asValue()) } /** * A stylesheet for this group and its descendants. Stylesheet is not applied directly, * but instead is just a repository for named configurations. */ -public val VisionGroup.styleSheet: StyleSheet get() = StyleSheet(this) +public val Vision.styleSheet: StyleSheet get() = StyleSheet(this) /** * Add style name to the list of styles to be resolved later. The style with given name does not necessary exist at the moment. */ public fun Vision.useStyle(name: String) { - styles = (meta.getMeta(Vision.STYLE_KEY)?.stringList ?: emptyList()) + name + styles = (getPropertyValue(Vision.STYLE_KEY)?.stringList ?: emptyList()) + name } /** - * Find a style with given name for given [Vision]. The style is not necessary applied to this [Vision]. + * Resolve a style with given name for given [Vision]. The style is not necessarily applied to this [Vision]. */ -public tailrec fun Vision.getStyle(name: String): Meta? = +public fun Vision.getStyle(name: String): Meta? = meta.getMeta(StyleSheet.STYLESHEET_KEY + name) ?: parent?.getStyle(name) /** * Resolve a property from all styles */ -public fun Vision.getStyleProperty(name: Name): Value? = styles.firstNotNullOfOrNull { getStyle(it)?.get(name)?.value } +public fun Vision.getStyleProperty(name: Name): Meta? = styles.firstNotNullOfOrNull { getStyle(it)?.get(name) } /** * Resolve an item in all style layers diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/Vision.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/Vision.kt index 1c8e3a28..b25f200c 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/Vision.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/Vision.kt @@ -1,17 +1,20 @@ package space.kscience.visionforge -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.channels.awaitClose -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.callbackFlow -import kotlinx.coroutines.launch -import space.kscience.dataforge.meta.* +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import space.kscience.dataforge.context.Global +import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.MutableMeta +import space.kscience.dataforge.meta.MutableMetaProvider import space.kscience.dataforge.meta.descriptors.Described import space.kscience.dataforge.meta.descriptors.MetaDescriptor -import space.kscience.dataforge.misc.DFExperimental +import space.kscience.dataforge.meta.descriptors.get import space.kscience.dataforge.misc.Type 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 space.kscience.dataforge.values.Value import space.kscience.dataforge.values.asValue @@ -23,38 +26,59 @@ import kotlin.reflect.KProperty1 * A root type for display hierarchy */ @Type(TYPE) -public interface Vision : Described, Configurable { +public interface Vision : Described { /** * The parent object of this one. If null, this one is a root. */ - public var parent: VisionGroup? + public var parent: Vision? /** * Owner [VisionManager]. Used to define coroutine scope a serialization */ - public val manager: VisionManager? get() = parent?.manager + public val manager: VisionManager get() = parent?.manager ?: Global.visionManager + + public val children: VisionChildren /** - * This Vision own properties (ignoring inheritance, styles and defaults) + * Own properties without inheritance or styles. */ - override val meta: ObservableMutableMeta + public val meta: Meta - /** - * Get property value with given layer flags. - * @param inherit toggles parent node property lookup. Null means inference from descriptor. Default is false. - * @param includeStyles toggles inclusion of properties from styles. default is true - */ public fun getPropertyValue( name: Name, - inherit: Boolean = false, - includeStyles: Boolean = true, - includeDefaults: Boolean = true, + inherit: Boolean, + includeStyles: Boolean, + includeDefaults: Boolean, ): Value? + /** + * Get property with given layer flags. + * @param inherit toggles parent node property lookup. Null means inference from descriptor. + * @param includeStyles toggles inclusion of properties from styles. + */ + public fun getProperty( + name: Name, + inherit: Boolean, + includeStyles: Boolean, + includeDefaults: Boolean, + ): MutableMeta = VisionProperties(this, name, descriptor?.get(name), inherit, includeStyles) + + public fun setProperty( + name: Name, + node: Meta?, + ) + + public fun setPropertyValue( + name: Name, + value: Value?, + ) + + public val propertyChanges: SharedFlow /** - * Notify all listeners that a property has been changed and should be invalidated + * Notify all listeners that a property has been changed and should be invalidated. + * This method does not check that the property has actually changed. */ public fun invalidateProperty(propertyName: Name) @@ -68,80 +92,135 @@ public interface Vision : Described, Configurable { public companion object { public const val TYPE: String = "vision" public val STYLE_KEY: Name = "@style".asName() + public const val STYLE_TARGET: String = "style" public val VISIBLE_KEY: Name = "visible".asName() } } -/** - * Flow of property invalidation events. It does not contain property values after invalidation since it is not clear - * if it should include inherited properties etc. - */ -@OptIn(ExperimentalCoroutinesApi::class) -@DFExperimental -public val Vision.propertyChanges: Flow - get() = callbackFlow { - meta.onChange(this) { name -> - launch { - send(name) - } - } - awaitClose { - meta.removeListener(this) - } - } +public fun Vision.getPropertyValue( + name: Name, + inherit: Boolean? = null, + includeStyles: Boolean? = null, + includeDefaults: Boolean = true, + metaDescriptor: MetaDescriptor? = descriptor?.get(name), +): Value? { + val inheritFlag = inherit ?: metaDescriptor?.inherited ?: false + val stylesFlag = includeStyles ?: metaDescriptor?.usesStyles ?: true + return getPropertyValue(name, inheritFlag, stylesFlag, includeDefaults) +} + +public fun Vision.getPropertyValue( + name: String, + inherit: Boolean? = null, + includeStyles: Boolean? = null, + includeDefaults: Boolean = true, + metaDescriptor: MetaDescriptor? = descriptor?.get(name), +): Value? = getPropertyValue(name.parseAsName(), inherit, includeStyles, includeDefaults, metaDescriptor) /** - * Subscribe on property updates. The subscription is bound to the given scope and canceled when the scope is canceled + * Compute the property based on the provided value descriptor. By default, use Vision own descriptor */ -public fun Vision.onPropertyChange(callback: Meta.(Name) -> Unit) { - meta.onChange(null, callback) +public fun Vision.getProperty( + name: Name, + inherit: Boolean? = null, + includeStyles: Boolean? = null, + includeDefaults: Boolean = true, + metaDescriptor: MetaDescriptor? = descriptor?.get(name), +): MutableMeta { + val inheritFlag = inherit ?: metaDescriptor?.inherited ?: false + val stylesFlag = includeStyles ?: metaDescriptor?.usesStyles ?: true + return getProperty(name, inheritFlag, stylesFlag, includeDefaults) } + /** * Get [Vision] property using key as a String */ -public fun Vision.getPropertyValue( - key: String, - inherit: Boolean = false, - includeStyles: Boolean = true, +public fun Vision.getProperty( + name: String, + inherit: Boolean? = null, + includeStyles: Boolean? = null, includeDefaults: Boolean = true, -): Value? = getPropertyValue(Name.parse(key), inherit, includeStyles, includeDefaults) + metaDescriptor: MetaDescriptor? = descriptor?.get(name), +): MutableMeta = getProperty(name.parseAsName(), inherit, includeStyles, includeDefaults, metaDescriptor) + /** - * A convenience method to set property node or value. If Item is null, then node is removed, not a value + * Vision's own non-inheritable, non-styleable properties */ -public fun Vision.setProperty(name: Name, item: Any?) { - when (item) { - null -> meta.remove(name) - is Meta -> meta.setMeta(name, item) - is Value -> meta.setValue(name, item) - else -> meta.setValue(name, Value.of(item)) +public fun Vision.properties( + inherit: Boolean? = null, + useStyles: Boolean? = null, +): MutableMetaProvider = VisionProperties(this, Name.EMPTY, inherit = inherit, useStyles = useStyles) + +public fun Vision.setPropertyValue(name: Name, value: Number?) { + if (value == null) { + setPropertyValue(name, null) + } else { + setPropertyValue(name, value.asValue()) } } -public fun Vision.setPropertyNode(key: String, item: Any?) { - setProperty(Name.parse(key), item) +public fun Vision.setPropertyValue(name: String, value: Number?): Unit = + setPropertyValue(name.parseAsName(), value) + +public fun Vision.setPropertyValue(name: Name, value: Boolean?) { + if (value == null) { + setPropertyValue(name, null) + } else { + setPropertyValue(name, value.asValue()) + } } +public fun Vision.setPropertyValue(name: String, value: Boolean?): Unit = + setPropertyValue(name.parseAsName(), value) + +public fun Vision.setPropertyValue(name: Name, value: String?) { + if (value == null) { + setPropertyValue(name, null) + } else { + setPropertyValue(name, value.asValue()) + } +} + +public fun Vision.setPropertyValue(name: String, value: String?): Unit = + setPropertyValue(name.parseAsName(), value) + /** * Control visibility of the element */ public var Vision.visible: Boolean? get() = getPropertyValue(Vision.VISIBLE_KEY)?.boolean - set(value) = meta.setValue(Vision.VISIBLE_KEY, value?.asValue()) + set(value) { + setPropertyValue(Vision.VISIBLE_KEY, value) + } + +/** + * Subscribe on property updates. The subscription is bound to the given scope and canceled when the scope is canceled + */ +public fun Vision.onPropertyChange(callback: (Name) -> Unit): Job = propertyChanges.onEach { + callback(it) +}.launchIn(manager.context) public fun V.useProperty( property: KProperty1, - owner: Any? = null, callBack: V.(T) -> Unit, -) { +): Job { //Pass initial value. callBack(property.get(this)) - meta.onChange(owner) { name -> + return propertyChanges.onEach { name -> if (name.startsWith(property.name.asName())) { callBack(property.get(this@useProperty)) } - } -} \ No newline at end of file + }.launchIn(manager.context) +} + + +public interface MutableVisionGroup : Vision { + + override val children: MutableVisionChildren + + public fun createGroup(): MutableVisionGroup +} diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionBase.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionBase.kt deleted file mode 100644 index 003fec79..00000000 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionBase.kt +++ /dev/null @@ -1,176 +0,0 @@ -package space.kscience.visionforge - -import kotlinx.serialization.EncodeDefault -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable -import kotlinx.serialization.Transient -import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.meta.MutableMeta -import space.kscience.dataforge.meta.ObservableMutableMeta -import space.kscience.dataforge.meta.descriptors.MetaDescriptor -import space.kscience.dataforge.meta.descriptors.value -import space.kscience.dataforge.meta.get -import space.kscience.dataforge.misc.DFExperimental -import space.kscience.dataforge.names.* -import space.kscience.dataforge.values.Value -import space.kscience.dataforge.values.ValueType -import space.kscience.visionforge.Vision.Companion.STYLE_KEY -import kotlin.jvm.Synchronized - -internal data class MetaListener( - val owner: Any? = null, - val callback: Meta.(name: Name) -> Unit, -) - -/** - * A full base implementation for a [Vision] - * @param parent the parent object for this vision. Could've set later. Not serialized. - */ -@Serializable -@SerialName("vision") -public open class VisionBase( - @Transient override var parent: VisionGroup? = null, - @EncodeDefault protected var properties: MutableMeta? = null, -) : Vision { - - @Synchronized - protected fun getOrCreateProperties(): MutableMeta { - if (properties == null) { - val newProperties = MutableMeta() - properties = newProperties - } - return properties!! - } - - @Transient - private val listeners: MutableList = mutableListOf() - - private inner class VisionProperties(val pathName: Name) : ObservableMutableMeta { - - override val items: Map - get() = properties?.get(pathName)?.items?.mapValues { entry -> - VisionProperties(pathName + entry.key) - } ?: emptyMap() - - override var value: Value? - get() = properties?.get(pathName)?.value - set(value) { - val oldValue = properties?.get(pathName)?.value - getOrCreateProperties().setValue(pathName, value) - if (oldValue != value) { - invalidate(Name.EMPTY) - } - } - - override fun getOrCreate(name: Name): ObservableMutableMeta = VisionProperties(pathName + name) - - override fun setMeta(name: Name, node: Meta?) { - getOrCreateProperties().setMeta(pathName + name, node) - invalidate(name) - } - - @DFExperimental - override fun attach(name: Name, node: ObservableMutableMeta) { - val ownProperties = getOrCreateProperties() - if (ownProperties is ObservableMutableMeta) { - ownProperties.attach(pathName + name, node) - } else { - ownProperties.setMeta(pathName + name, node) - node.onChange(this) { childName -> - ownProperties.setMeta(pathName + name + childName, this[childName]) - } - } - } - - override fun invalidate(name: Name) { - invalidateProperty(pathName + name) - } - - @Synchronized - override fun onChange(owner: Any?, callback: Meta.(name: Name) -> Unit) { - if (pathName.isEmpty()) { - listeners.add((MetaListener(owner, callback))) - } else { - listeners.add(MetaListener(owner) { name -> - if (name.startsWith(pathName)) { - (this@MetaListener[pathName] ?: Meta.EMPTY).callback(name.removeHeadOrNull(pathName)!!) - } - }) - } - } - - @Synchronized - override fun removeListener(owner: Any?) { - listeners.removeAll { it.owner === owner } - } - - override fun toString(): String = Meta.toString(this) - override fun equals(other: Any?): Boolean = Meta.equals(this, other as? Meta) - override fun hashCode(): Int = Meta.hashCode(this) - } - - final override val meta: ObservableMutableMeta get() = VisionProperties(Name.EMPTY) - - override fun getPropertyValue( - name: Name, - inherit: Boolean, - includeStyles: Boolean, - includeDefaults: Boolean, - ): Value? { - properties?.get(name)?.value?.let { return it } - if (includeStyles) { - getStyleProperty(name)?.let { return it } - } - if (inherit) { - parent?.getPropertyValue(name, inherit, includeStyles, includeDefaults)?.let { return it } - } - if (includeDefaults) { - descriptor?.defaultNode?.get(name)?.value.let { return it } - } - return null - } - - override val descriptor: MetaDescriptor? get() = null - - override fun invalidateProperty(propertyName: Name) { - if (propertyName == STYLE_KEY) { - styles.mapNotNull { getStyle(it) }.asSequence() - .flatMap { it.items.asSequence() } - .distinctBy { it.key } - .forEach { - invalidateProperty(it.key.asName()) - } - } - listeners.forEach { it.callback(properties ?: Meta.EMPTY, propertyName) } - } - - override fun update(change: VisionChange) { - change.properties?.let { - updateProperties(Name.EMPTY, it) - } - } - - public companion object { - public val descriptor: MetaDescriptor = MetaDescriptor { - value(STYLE_KEY, ValueType.STRING) { - multiple = true - } - } - - public fun Vision.updateProperties(at: Name, item: Meta) { - meta.setValue(at, item.value) - item.items.forEach { (token, item) -> - updateProperties(at + token, item) - } - } - - } -} - -//fun VisualObject.findStyle(styleName: Name): Meta? { -// if (this is VisualGroup) { -// val style = resolveStyle(styleName) -// if (style != null) return style -// } -// return parent?.findStyle(styleName) -//} \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionChange.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionChange.kt index ad0d0d70..abea638a 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionChange.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionChange.kt @@ -7,7 +7,6 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.serialization.Serializable import space.kscience.dataforge.meta.* -import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.plus import space.kscience.dataforge.values.Null @@ -19,14 +18,13 @@ import kotlin.time.Duration */ private fun Vision.deepCopy(): Vision { //Assuming that unrooted visions are already isolated - val manager = this.manager ?: return this //TODO replace by efficient deep copy val json = manager.encodeToJsonElement(this) return manager.decodeFromJson(json) } /** - * An update for a [Vision] or a [VisionGroup] + * An update for a [Vision] */ public class VisionChangeBuilder : VisionContainerBuilder { @@ -87,7 +85,6 @@ public inline fun VisionChange(block: VisionChangeBuilder.() -> Unit): VisionCha VisionChangeBuilder().apply(block).deepCopy() -@OptIn(DFExperimental::class) private fun CoroutineScope.collectChange( name: Name, source: Vision, @@ -96,28 +93,25 @@ private fun CoroutineScope.collectChange( //Collect properties change source.onPropertyChange { propertyName -> - val newItem = source.meta[propertyName] + val newItem = source.getProperty(propertyName, false, false, false) collector().propertyChanged(name, propertyName, newItem) } - if (source is VisionGroup) { - //Subscribe for children changes - source.children.forEach { (token, child) -> - collectChange(name + token, child, collector) - } - - //Subscribe for structure change - if (source is MutableVisionGroup) { - source.structureChanges.onEach { changedName -> - val after = source[changedName] - val fullName = name + changedName - if (after != null) { - collectChange(fullName, after, collector) - } - collector()[fullName] = after - }.launchIn(this) - } + val children = source.children + //Subscribe for children changes + for ((token, child) in children) { + collectChange(name + token, child, collector) } + + //Subscribe for structure change + children.changes.onEach { changedName -> + val after = children[changedName] + val fullName = name + changedName + if (after != null) { + collectChange(fullName, after, collector) + } + collector()[fullName] = after + }.launchIn(this) } /** diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionContainer.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionContainer.kt new file mode 100644 index 00000000..f7056d59 --- /dev/null +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionContainer.kt @@ -0,0 +1,196 @@ +package space.kscience.visionforge + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.* +import kotlinx.coroutines.launch +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializable +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import kotlinx.serialization.serializer +import space.kscience.dataforge.names.* + +@DslMarker +public annotation class VisionBuilder + +public interface VisionContainer { + public operator fun get(name: Name): V? +} + +public interface VisionContainerBuilder { + //TODO add documentation + public operator fun set(name: Name?, child: V?) +} + +/** + * A serializable representation of [Vision] children container + */ +public interface VisionChildren : VisionContainer { + public val parent: Vision? + + public val keys: Set + + public val values: Iterable get() = keys.map { get(it)!! } + + public val changes: Flow + + public operator fun get(token: NameToken): Vision? + + override fun get(name: Name): Vision? = when (name.length) { + 0 -> parent + 1 -> get(name.first()) + else -> get(name.first())?.children?.get(name.cutFirst()) + } + + public companion object { + public fun empty(owner: Vision): VisionChildren = object : VisionChildren { + override val parent: Vision get() = owner + override val keys: Set get() = emptySet() + override val changes: Flow get() = emptyFlow() + override fun get(token: NameToken): Vision? = null + } + } +} + +public fun VisionChildren.isEmpty(): Boolean = keys.isEmpty() + +@Serializable(VisionChildrenContainerSerializer::class) +public interface MutableVisionChildren : VisionChildren, VisionContainerBuilder { + public override val parent: MutableVisionGroup? + + public operator fun set(token: NameToken, value: Vision?) + + override fun set(name: Name?, child: Vision?) { + when { + name == null -> { + if (child != null) { + static(child) + } + } + + name.isEmpty() -> error("Empty names are not allowed in VisionGroup::set") + name.length == 1 -> { + val token = name.tokens.first() + set(token, child) + } + + else -> { + val currentParent = get(name.first()) + if (currentParent != null && currentParent !is MutableVisionGroup) error("Can't assign a child to $currentParent") + val parent: MutableVisionGroup = currentParent as? MutableVisionGroup ?: parent?.createGroup().also { + set(name.first(), it) + } ?: error("Container owner not set") + parent.children[name.cutFirst()] = child + } + } + } + + public fun clear() +} + +/** + * Add a static child. Statics could not be found by name, removed or replaced. Changing statics also do not trigger events. + */ +public fun MutableVisionChildren.static(child: Vision): Unit { + set(NameToken("@static", index = child.hashCode().toString()), child) +} + +public fun VisionChildren.asSequence(): Sequence> = sequence { + keys.forEach { yield(it to get(it)!!) } +} + +public operator fun VisionChildren.iterator(): Iterator> = asSequence().iterator() + +public operator fun VisionContainer.get(str: String): V? = get(Name.parse(str)) + +public operator fun VisionContainerBuilder.set( + str: String?, vision: V?, +): Unit = set(str?.parseAsName(), vision) + +internal class VisionChildrenImpl( + items: Map, +) : MutableVisionChildren { + + override var parent: MutableVisionGroup? = null + internal set + + private val items = LinkedHashMap(items) + private val updateJobs = HashMap() + + private val scope: CoroutineScope? get() = parent?.manager?.context + + override val keys: Set get() = items.keys + + override fun get(token: NameToken): Vision? = items[token] + + private val _changes = MutableSharedFlow() + override val changes: SharedFlow get() = _changes + + private fun onChange(name: Name) { + scope?.launch { + _changes.emit(name) + } + } + + override operator fun set(token: NameToken, value: Vision?) { + //fast return if value equals existing + if (value == get(token)) return + + val currentUpdateJob = updateJobs[token] + if (currentUpdateJob != null) { + currentUpdateJob.cancel() + updateJobs.remove(token) + } + + if (value == null) { + items.remove(token) + } else { + items[token] = value + //check if parent already exists and is different from the current one + if (value.parent != null && value.parent != parent) error("Can't reassign parent Vision for $value") + //set parent + value.parent = parent + //start update jobs (only if the vision is rooted) + scope?.let { scope -> + val job = (value.children as? VisionChildrenImpl)?.changes?.onEach { + onChange(token + it) + }?.launchIn(scope) + if (job != null) { + updateJobs[token] = job + } + } + } + + onChange(token.asName()) + } + + override fun clear() { + if (items.isNotEmpty()) { + updateJobs.values.forEach { + it.cancel() + } + updateJobs.clear() + items.clear() + onChange(Name.EMPTY) + } + } +} + +internal object VisionChildrenContainerSerializer : KSerializer { + private val mapSerializer = serializer>() + + override val descriptor: SerialDescriptor = mapSerializer.descriptor + + override fun deserialize(decoder: Decoder): MutableVisionChildren { + val map = decoder.decodeSerializableValue(mapSerializer) + return VisionChildrenImpl(map) + } + + override fun serialize(encoder: Encoder, value: MutableVisionChildren) { + val map = value.keys.associateWith { value[it]!! } + encoder.encodeSerializableValue(mapSerializer, map) + } + +} diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroup.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroup.kt index a8ad1dcd..3c843b85 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroup.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroup.kt @@ -1,109 +1,99 @@ package space.kscience.visionforge -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.callbackFlow -import kotlinx.coroutines.launch -import space.kscience.dataforge.misc.DFExperimental -import space.kscience.dataforge.names.* -import space.kscience.dataforge.provider.Provider - -@DslMarker -public annotation class VisionBuilder - -public interface VisionContainer { - public operator fun get(name: Name): V? -} +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlinx.serialization.Transient +import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.descriptors.MetaDescriptor +import space.kscience.dataforge.meta.descriptors.value +import space.kscience.dataforge.names.Name +import space.kscience.dataforge.names.NameToken +import space.kscience.dataforge.names.plus +import space.kscience.dataforge.values.ValueType +import space.kscience.visionforge.Vision.Companion.STYLE_KEY +import kotlin.jvm.Synchronized /** - * Represents a group of [Vision] instances + * A full base implementation for a [Vision] */ -public interface VisionGroup : Provider, Vision, VisionContainer { - /** - * A map of top level named children - */ - public val children: Map +@Serializable +@SerialName("vision.group") +public open class VisionGroup : AbstractVision(), MutableVisionGroup { - override val defaultTarget: String get() = Vision.TYPE - - /** - * A map of direct children for specific target - * (currently "visual" or "style") - */ - override fun content(target: String): Map = - when (target) { - Vision.TYPE -> children.flatMap { (key, value) -> - val res: Map = if (value is VisionGroup) { - value.content(target).mapKeys { key + it.key } - } else { - mapOf(key.asName() to value) - } - res.entries - }.associate { it.toPair() } - STYLE_TARGET -> styleSheet.items?.mapKeys { it.key.asName() } ?: emptyMap() - else -> emptyMap() - } - - public override operator fun get(name: Name): Vision? { - return when { - name.isEmpty() -> this - name.length == 1 -> children[name.tokens.first()] - else -> (children[name.tokens.first()] as? VisionGroup)?.get(name.cutFirst()) - } - } - - public companion object { - public const val STYLE_TARGET: String = "style" - } -} - -/** - * Iterate over children of this group - */ -public operator fun VisionGroup.iterator(): Iterator = children.values.iterator() - -public fun VisionGroup.isEmpty(): Boolean = this.children.isEmpty() - -public interface VisionContainerBuilder { - //TODO add documentation - public operator fun set(name: Name?, child: V?) -} - -/** - * Mutable version of [VisionGroup] - */ -public interface MutableVisionGroup : VisionGroup, VisionContainerBuilder { - public fun onStructureChanged(owner: Any?, block: VisionGroup.(Name) -> Unit) - - public fun removeStructureListener(owner: Any?) -} - - -/** - * Flow structure changes of this group. Unconsumed changes are discarded - */ -@OptIn(ExperimentalCoroutinesApi::class) -@DFExperimental -public val MutableVisionGroup.structureChanges: Flow - get() = callbackFlow { - meta.onChange(this) { name -> - launch { - send(name) + override fun update(change: VisionChange) { + change.children?.forEach { (name, change) -> + when { + change.delete -> children.set(name, null) + change.vision != null -> children.set(name, change.vision) + else -> children[name]?.update(change) } } - awaitClose { - removeStructureListener(this) + change.properties?.let { + updateProperties(it, Name.EMPTY) } } + @SerialName("children") + protected var _children: MutableVisionChildren? = null -public operator fun VisionContainer.get(str: String): V? = get(Name.parse(str)) + @Transient + override val children: MutableVisionChildren = object : MutableVisionChildren { -public operator fun VisionContainerBuilder.set(token: NameToken, child: V?): Unit = - set(token.asName(), child) + @Synchronized + fun getOrCreateChildren(): MutableVisionChildren { + if (_children == null) { + _children = VisionChildrenImpl(emptyMap()).apply { + parent = this@VisionGroup + } + } + return _children!! + } -public operator fun VisionContainerBuilder.set(key: String?, child: V?): Unit = - set(key?.let(Name::parse), child) + override val parent: MutableVisionGroup get() = this@VisionGroup -public fun MutableVisionGroup.removeAll(): Unit = children.keys.map { it.asName() }.forEach { this[it] = null } \ No newline at end of file + override val keys: Set get() = _children?.keys ?: emptySet() + override val changes: Flow get() = _children?.changes ?: emptyFlow() + + override fun get(token: NameToken): Vision? = _children?.get(token) + + override fun set(token: NameToken, value: Vision?) { + getOrCreateChildren()[token] = value + } + + override fun set(name: Name?, child: Vision?) { + getOrCreateChildren()[name] = child + } + + override fun clear() { + _children?.clear() + } + } + + override fun createGroup(): VisionGroup = VisionGroup() + + public companion object { + public val descriptor: MetaDescriptor = MetaDescriptor { + value(STYLE_KEY, ValueType.STRING) { + multiple = true + } + } + + public fun Vision.updateProperties(item: Meta, at: Name = Name.EMPTY) { + setPropertyValue(at, item.value) + item.items.forEach { (token, item) -> + updateProperties(item, at + token) + } + } + + } +} + +//fun VisualObject.findStyle(styleName: Name): Meta? { +// if (this is VisualGroup) { +// val style = resolveStyle(styleName) +// if (style != null) return style +// } +// return parent?.findStyle(styleName) +//} \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroupBase.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroupBase.kt deleted file mode 100644 index 7c2af29e..00000000 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroupBase.kt +++ /dev/null @@ -1,168 +0,0 @@ -package space.kscience.visionforge - -import kotlinx.serialization.EncodeDefault -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable -import kotlinx.serialization.Transient -import space.kscience.dataforge.names.* -import kotlin.jvm.Synchronized - -private class StructureChangeListener(val owner: Any?, val callback: VisionGroup.(Name) -> Unit) - -/** - * Abstract implementation of mutable group of [Vision] - * - * @param childrenInternal Internal mutable container for group children - */ -@Serializable -@SerialName("vision.group") -public open class VisionGroupBase( - @EncodeDefault @SerialName("children") protected val childrenInternal: MutableMap = LinkedHashMap(), -) : VisionBase(), MutableVisionGroup { - - /** - * A map of top level named children - */ - override val children: Map get() = childrenInternal - - init { - childrenInternal.forEach { (token, child) -> - if (child.parent != null && child.parent != this) error("Can't reassign existing parent for child $token") - child.parent = this - } - } - - override fun invalidateProperty(propertyName: Name) { - super.invalidateProperty(propertyName) - for (obj in this) { - obj.invalidateProperty(propertyName) - } - } - - @Transient - private val structureListeners = HashSet() - - @Synchronized - override fun onStructureChanged(owner: Any?, block: VisionGroup.(Name) -> Unit) { - structureListeners.add(StructureChangeListener(owner, block)) - } - - @Synchronized - override fun removeStructureListener(owner: Any?) { - structureListeners.removeAll { it.owner == owner } - } - - /** - * Propagate children change event upwards - */ - protected fun childrenChanged(name: Name) { - structureListeners.forEach { - it.callback(this, name) - } - } - - /** - * Add a static child. Statics could not be found by name, removed or replaced. Changing statics also do not trigger events. - */ - protected open fun addStatic(child: Vision): Unit { - attachChild(NameToken("@static", index = child.hashCode().toString()), child) - } - - /** - * Create a vision group of the same type as this vision group. Do not attach it. - */ - protected open fun createGroup(): VisionGroupBase = VisionGroupBase() - - /** - * Set parent for given child and attach it - */ - private fun attachChild(token: NameToken, child: Vision?) { - val before = childrenInternal[token] - when { - child == null -> { - childrenInternal.remove(token) - } - child.parent == null -> { - child.parent = this - childrenInternal[token] = child - } - child.parent !== this -> { - error("Can't reassign existing parent for child $token") - } - } - if (before != child) { - childrenChanged(token.asName()) - if (child is MutableVisionGroup) { - child.onStructureChanged(this) { changedName -> - this@VisionGroupBase.childrenChanged(token + changedName) - } - } - } - } - - /** - * Recursively create a child group - */ - private fun createGroups(name: Name): VisionGroupBase = when { - name.isEmpty() -> error("Should be unreachable") - name.length == 1 -> { - val token = name.tokens.first() - when (val current = children[token]) { - null -> createGroup().also { child -> - attachChild(token, child) - } - is VisionGroupBase -> current - else -> error("Can't create group with name $name because it exists and not a group") - } - } - else -> createGroups(name.tokens.first().asName()).createGroups(name.cutFirst()) - } - - /** - * Add named or unnamed child to the group. If key is null the child is considered unnamed. Both key and value are not - * allowed to be null in the same time. If name is present and [child] is null, the appropriate element is removed. - */ - override fun set(name: Name?, child: Vision?): Unit { - when { - name == null -> { - if (child != null) { - addStatic(child) - } - } - name.isEmpty() -> error("Empty names are not allowed in VisionGroup::set") - name.length == 1 -> { - val token = name.tokens.first() - attachChild(token, child) - } - else -> { - //TODO add safety check - val parent = (get(name.cutLast()) as? MutableVisionGroup) ?: createGroups(name.cutLast()) - parent[name.tokens.last().asName()] = child - } - } - } - - override fun update(change: VisionChange) { - change.children?.forEach { (name, change) -> - when { - change.delete -> set(name, null) - change.vision != null -> set(name, change.vision) - else -> get(name)?.update(change) - } - } - super.update(change) - } -} - -/** - * Non-serializable root group used to propagate manager to its children - */ -internal class RootVisionGroup(override val manager: VisionManager) : VisionGroupBase() - -/** - * Designate this [VisionGroup] as a root and assign a [VisionManager] as its parent - */ -public fun Vision.setAsRoot(manager: VisionManager) { - if (parent != null) error("Vision $this already has a parent. It could not be set as root") - parent = RootVisionGroup(manager) -} \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionManager.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionManager.kt index cd017cad..a11ad358 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionManager.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionManager.kt @@ -25,13 +25,14 @@ public class VisionManager(meta: Meta) : AbstractPlugin(meta) { /** * Combined [SerializersModule] for all registered visions */ - public val serializersModule: SerializersModule - get() = SerializersModule { + public val serializersModule: SerializersModule by lazy { + SerializersModule { include(defaultSerialModule) context.gather(VISION_SERIALIZER_MODULE_TARGET).values.forEach { include(it) } } + } public val jsonFormat: Json get() = Json(defaultJson) { @@ -67,9 +68,8 @@ public class VisionManager(meta: Meta) : AbstractPlugin(meta) { private val defaultSerialModule: SerializersModule = SerializersModule { polymorphic(Vision::class) { - default { VisionBase.serializer() } - subclass(VisionBase.serializer()) - subclass(VisionGroupBase.serializer()) + default { VisionGroup.serializer() } + subclass(VisionGroup.serializer()) subclass(VisionOfNumberField.serializer()) subclass(VisionOfTextField.serializer()) subclass(VisionOfCheckbox.serializer()) @@ -107,5 +107,17 @@ public abstract class VisionPlugin(meta: Meta = Meta.EMPTY) : AbstractPlugin(met */ public val Context.visionManager: VisionManager get() = fetch(VisionManager) -public fun Vision.encodeToString(): String = - manager?.encodeToString(this) ?: error("VisionManager not defined in Vision") \ No newline at end of file +public fun Vision.encodeToString(): String = manager.encodeToString(this) + +/** + * A root vision attached to [VisionManager] + */ +public class RootVision(override val manager: VisionManager) : VisionGroup() + +/** + * Designate this [Vision] as a root and assign a [VisionManager] as its parent + */ +public fun Vision.setAsRoot(manager: VisionManager) { + if (parent != null) error("Vision $this already has a parent. It could not be set as root") + parent = RootVision(manager) +} \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionProperties.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionProperties.kt new file mode 100644 index 00000000..140170d3 --- /dev/null +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionProperties.kt @@ -0,0 +1,81 @@ +package space.kscience.visionforge + +import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.MutableMeta +import space.kscience.dataforge.meta.descriptors.MetaDescriptor +import space.kscience.dataforge.meta.descriptors.get +import space.kscience.dataforge.names.Name +import space.kscience.dataforge.names.NameToken +import space.kscience.dataforge.names.plus +import space.kscience.dataforge.values.Value + +/** + * A wrapper that emulates delegates reading and writing properties to Vision method + */ +internal class VisionProperties( + val vision: Vision, + val nodeName: Name, + val visionDescriptor: MetaDescriptor? = vision.descriptor, + val inherit: Boolean? = null, + val useStyles: Boolean? = null, +) : MutableMeta { + + val descriptor: MetaDescriptor? by lazy { visionDescriptor?.get(nodeName) } + + override val items: Map + get() { + val metaKeys = vision.meta.getMeta(nodeName)?.items?.keys ?: emptySet() + val descriptorKeys = descriptor?.children?.map { NameToken(it.key) } ?: emptySet() + val inheritFlag = descriptor?.inherited ?: inherit + val stylesFlag = descriptor?.usesStyles ?: useStyles + return (metaKeys + descriptorKeys).associateWith { + VisionProperties( + vision, + nodeName + it, + visionDescriptor, + inheritFlag, + stylesFlag + ) + } + } + + override var value: Value? + get() { + val inheritFlag = descriptor?.inherited ?: inherit ?: false + val stylesFlag = descriptor?.usesStyles ?: useStyles ?: true + return vision.getPropertyValue(nodeName, inheritFlag, stylesFlag, true) + } + set(value) { + vision.setPropertyValue(nodeName, value) + } + + override fun getOrCreate(name: Name): MutableMeta = VisionProperties( + vision, + nodeName + name, + visionDescriptor, + inherit, + useStyles + ) + + override fun setMeta(name: Name, node: Meta?) { + vision.setProperty(nodeName + name, node) + } + + override fun toString(): String = Meta.toString(this) + override fun equals(other: Any?): Boolean = Meta.equals(this, other as? Meta) + override fun hashCode(): Int = Meta.hashCode(this) +} + +///** +// * Accessor to all vision properties +// */ +//public fun Vision.computePropertyValues( +// descriptor: MetaDescriptor? = this.descriptor, +//): MutableValueProvider = object : MutableValueProvider { +// override fun getValue(name: Name): Value? = computeProperty(name, descriptor?.get(name))?.value +// +// override fun setValue(name: Name, value: Value?) { +// setProperty(name, value) +// } +//} + diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionPropertyContainer.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionPropertyContainer.kt index fed474fc..83b6c93c 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionPropertyContainer.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionPropertyContainer.kt @@ -1,34 +1,29 @@ package space.kscience.visionforge -import space.kscience.dataforge.meta.Configurable +import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.MutableMeta -import space.kscience.dataforge.meta.ObservableMutableMeta -import space.kscience.dataforge.meta.get import space.kscience.dataforge.names.Name -import space.kscience.dataforge.values.Value /** * Property containers are used to create a symmetric behaviors for vision properties and style builders */ public interface VisionPropertyContainer { - public val meta: MutableMeta - - public fun getPropertyValue( - name: Name, - inherit: Boolean = false, - includeStyles: Boolean = true, - includeDefaults: Boolean = true, - ): Value? -} - -public open class SimpleVisionPropertyContainer( - override val meta: ObservableMutableMeta, -) : VisionPropertyContainer, Configurable { - override fun getPropertyValue( + public fun getProperty( name: Name, inherit: Boolean, includeStyles: Boolean, - includeDefaults: Boolean - ): Value? = meta[name]?.value + includeDefaults: Boolean, + ): Meta? +} + +public open class SimpleVisionPropertyContainer( + public val meta: MutableMeta, +) : VisionPropertyContainer { + override fun getProperty( + name: Name, + inherit: Boolean, + includeStyles: Boolean, + includeDefaults: Boolean, + ): Meta? = meta.getMeta(name) } \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/VisionOfHtmlForm.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/VisionOfHtmlForm.kt index 0ef6f54e..82e59c2c 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/VisionOfHtmlForm.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/VisionOfHtmlForm.kt @@ -9,13 +9,14 @@ import kotlinx.serialization.Serializable import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.get import space.kscience.dataforge.meta.node +import space.kscience.visionforge.properties @Serializable @SerialName("html.form") public class VisionOfHtmlForm( public val formId: String, ) : VisionOfHtmlInput() { - public var values: Meta? by meta.node() + public var values: Meta? by properties().node() } public class HtmlFormFragment internal constructor( diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/VisionOfHtmlInput.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/VisionOfHtmlInput.kt index 084c5b6b..34f49027 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/VisionOfHtmlInput.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/VisionOfHtmlInput.kt @@ -5,11 +5,12 @@ import kotlinx.serialization.Serializable import space.kscience.dataforge.meta.boolean import space.kscience.dataforge.meta.number import space.kscience.dataforge.meta.string -import space.kscience.visionforge.VisionBase +import space.kscience.visionforge.VisionGroup +import space.kscience.visionforge.properties @Serializable -public abstract class VisionOfHtmlInput : VisionBase() { - public var disabled: Boolean by meta.boolean(false) +public abstract class VisionOfHtmlInput : VisionGroup() { + public var disabled: Boolean by properties().boolean(false) } @Serializable @@ -18,7 +19,7 @@ public class VisionOfTextField( public val label: String? = null, public val name: String? = null, ) : VisionOfHtmlInput() { - public var text: String? by meta.string() + public var text: String? by properties().string() } @Serializable @@ -27,7 +28,7 @@ public class VisionOfCheckbox( public val label: String? = null, public val name: String? = null, ) : VisionOfHtmlInput() { - public var checked: Boolean? by meta.boolean() + public var checked: Boolean? by properties().boolean() } @Serializable @@ -36,7 +37,7 @@ public class VisionOfNumberField( public val label: String? = null, public val name: String? = null, ) : VisionOfHtmlInput() { - public var value: Number? by meta.number() + public var value: Number? by properties().number() } @Serializable @@ -48,6 +49,6 @@ public class VisionOfRangeField( public val label: String? = null, public val name: String? = null, ) : VisionOfHtmlInput() { - public var value: Number? by meta.number() + public var value: Number? by properties().number() } diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/visionDelegates.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/visionDelegates.kt index 999bbb45..185c98a9 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/visionDelegates.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/visionDelegates.kt @@ -49,7 +49,7 @@ public fun Vision.propertyValue( getPropertyValue(name ?: Name.parse(property.name), inherit, includeStyles, includeDefaults) override fun setValue(thisRef: Any?, property: KProperty<*>, value: Value?) { - meta.setValue(name ?: Name.parse(property.name), value) + setPropertyValue(name ?: Name.parse(property.name), value) } } @@ -69,7 +69,7 @@ public fun Vision.propertyValue( ).let(getter) override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { - meta.setValue(name ?: Name.parse(property.name), value?.let(setter)) + setPropertyValue(name ?: Name.parse(property.name), value?.let(setter)) } } diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/visionDescriptor.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/visionDescriptor.kt index 15ef9229..3fa6703f 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/visionDescriptor.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/visionDescriptor.kt @@ -3,6 +3,7 @@ package space.kscience.visionforge import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.descriptors.* import space.kscience.dataforge.values.asValue +import space.kscience.dataforge.values.set private const val INHERITED_DESCRIPTOR_ATTRIBUTE = "inherited" private const val STYLE_DESCRIPTOR_ATTRIBUTE = "useStyles" diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/visitor/StatisticsVisitor.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/visitor/StatisticsVisitor.kt index 0a60c3c0..5b989274 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/visitor/StatisticsVisitor.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/visitor/StatisticsVisitor.kt @@ -1,6 +1,5 @@ package space.kscience.visionforge.visitor -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow @@ -11,7 +10,6 @@ import space.kscience.visionforge.Vision import kotlin.reflect.KClass -@OptIn(ExperimentalCoroutinesApi::class) public suspend fun Vision.flowStatistics(statistics: (Name, Vision) -> T): Flow = callbackFlow { val visitor = object : VisionVisitor { override suspend fun visit(name: Name, vision: Vision){ diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/visitor/VisionVisitor.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/visitor/VisionVisitor.kt index 0533dc4e..3ba313a9 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/visitor/VisionVisitor.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/visitor/VisionVisitor.kt @@ -6,7 +6,7 @@ import kotlinx.coroutines.launch import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.plus import space.kscience.visionforge.Vision -import space.kscience.visionforge.VisionGroup +import space.kscience.visionforge.iterator public interface VisionVisitor { /** @@ -19,30 +19,30 @@ public interface VisionVisitor { /** * Rearrange children of given group */ - public suspend fun visitChildren(name: Name, group: VisionGroup) { + public suspend fun visitChildren(name: Name, group: Vision) { //Do nothing by default } public fun skip(name: Name, vision: Vision): Boolean = false - public companion object{ + public companion object { private fun CoroutineScope.visitTreeAsync( visionVisitor: VisionVisitor, name: Name, - vision: Vision + vision: Vision, ): Job = launch { if (visionVisitor.skip(name, vision)) return@launch visionVisitor.visit(name, vision) - if (vision is VisionGroup) { - visionVisitor.visitChildren(name, vision) - for ((token, child) in vision.children) { - visitTreeAsync(visionVisitor, name + token, child) - } + visionVisitor.visitChildren(name, vision) + + for ((token, child) in vision.children) { + visitTreeAsync(visionVisitor, name + token, child) } } + /** * Recursively visit this [Vision] and all children */ diff --git a/visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/html/HtmlTagTest.kt b/visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/html/HtmlTagTest.kt index 42a9ba1f..b7b8208d 100644 --- a/visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/html/HtmlTagTest.kt +++ b/visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/html/HtmlTagTest.kt @@ -4,13 +4,9 @@ import kotlinx.html.* import kotlinx.html.stream.createHTML import space.kscience.dataforge.context.Global import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.meta.configure -import space.kscience.dataforge.meta.set import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.names.Name -import space.kscience.visionforge.Vision -import space.kscience.visionforge.VisionBase -import space.kscience.visionforge.VisionManager +import space.kscience.visionforge.* import kotlin.collections.set import kotlin.test.Test @@ -36,7 +32,7 @@ fun FlowContent.renderVisionFragment( @DFExperimental class HtmlTagTest { - fun VisionOutput.base(block: VisionBase.() -> Unit) = VisionBase().apply(block) + fun VisionOutput.base(block: VisionGroup.() -> Unit) = VisionGroup().apply(block) val fragment: HtmlVisionFragment = { div { @@ -46,10 +42,8 @@ class HtmlTagTest { "metaProperty" put 87 } base { - configure { - set("myProp", 82) - set("otherProp", false) - } + setPropertyValue("myProp", 82) + setPropertyValue("otherProp", false) } } } @@ -59,7 +53,7 @@ class HtmlTagTest { div { h2 { +"Properties" } ul { - (vision as? VisionBase)?.meta?.items?.forEach { + vision.getProperty(Name.EMPTY).items.forEach { li { a { +it.key.toString() } p { +it.value.toString() } diff --git a/visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/meta/VisionPropertyTest.kt b/visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/meta/VisionPropertyTest.kt index 40b4c96e..234d0043 100644 --- a/visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/meta/VisionPropertyTest.kt +++ b/visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/meta/VisionPropertyTest.kt @@ -1,8 +1,16 @@ package space.kscience.visionforge.meta -import space.kscience.dataforge.meta.* +import space.kscience.dataforge.meta.Scheme +import space.kscience.dataforge.meta.SchemeSpec +import space.kscience.dataforge.meta.int +import space.kscience.dataforge.meta.updateWith import space.kscience.dataforge.values.asValue -import space.kscience.visionforge.VisionBase +import space.kscience.dataforge.values.boolean +import space.kscience.dataforge.values.int +import space.kscience.visionforge.VisionGroup +import space.kscience.visionforge.getProperty +import space.kscience.visionforge.getPropertyValue +import space.kscience.visionforge.setPropertyValue import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNotEquals @@ -10,22 +18,22 @@ import kotlin.test.assertNotEquals class VisionPropertyTest { @Test fun testPropertyWrite(){ - val vision = VisionBase() - vision.meta["fff"] = 2 - vision.meta["fff.ddd"] = false + val vision = VisionGroup() + vision.setPropertyValue("fff", 2) + vision.setPropertyValue("fff.ddd", false) - assertEquals(2, vision.meta["fff"]?.int) - assertEquals(false, vision.meta["fff.ddd"]?.boolean) + assertEquals(2, vision.getPropertyValue("fff")?.int) + assertEquals(false, vision.getPropertyValue("fff.ddd")?.boolean) } @Test fun testPropertyEdit(){ - val vision = VisionBase() - vision.meta.getOrCreate("fff.ddd").apply { + val vision = VisionGroup() + vision.getProperty("fff.ddd").apply { value = 2.asValue() } - assertEquals(2, vision.meta["fff.ddd"]?.int) - assertNotEquals(true, vision.meta["fff.ddd"]?.boolean) + assertEquals(2, vision.getPropertyValue("fff.ddd")?.int) + assertNotEquals(true, vision.getPropertyValue("fff.ddd")?.boolean) } internal class TestScheme: Scheme(){ @@ -35,10 +43,10 @@ class VisionPropertyTest { @Test fun testPropertyUpdate(){ - val vision = VisionBase() - vision.meta.getOrCreate("fff").updateWith(TestScheme){ + val vision = VisionGroup() + vision.getProperty("fff").updateWith(TestScheme){ ddd = 2 } - assertEquals(2, vision.meta["fff.ddd"]?.int) + assertEquals(2, vision.getPropertyValue("fff.ddd")?.int) } } \ No newline at end of file diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/VisionEditorFragment.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/VisionEditorFragment.kt index bf1033ba..7b1299f4 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/VisionEditorFragment.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/VisionEditorFragment.kt @@ -6,11 +6,10 @@ import javafx.scene.Node import javafx.scene.Parent import javafx.scene.layout.VBox import space.kscience.dataforge.meta.MutableMeta -import space.kscience.dataforge.meta.ObservableMutableMeta import space.kscience.dataforge.meta.descriptors.MetaDescriptor import space.kscience.dataforge.names.Name import space.kscience.visionforge.Vision -import space.kscience.visionforge.computeProperties +import space.kscience.visionforge.getProperty import space.kscience.visionforge.getStyle import space.kscience.visionforge.styles import tornadofx.* @@ -21,8 +20,8 @@ public class VisionEditorFragment : Fragment() { public var vision: Vision? by visionProperty public val descriptorProperty: SimpleObjectProperty = SimpleObjectProperty() - private val configProperty: Binding = visionProperty.objectBinding { vision -> - vision?.meta + private val configProperty: Binding = visionProperty.objectBinding { vision -> + vision?.getProperty(Name.EMPTY) } private val configEditorProperty: Binding = configProperty.objectBinding(descriptorProperty) { @@ -30,7 +29,7 @@ public class VisionEditorFragment : Fragment() { val node:FXMetaModel = FXMetaModel( meta, vision?.descriptor, - vision?.computeProperties(), + vision?.meta, Name.EMPTY, "Vision properties" ) diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/VisionTreeFragment.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/VisionTreeFragment.kt index 335b5a69..c0c1dfa4 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/VisionTreeFragment.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/VisionTreeFragment.kt @@ -5,17 +5,17 @@ import javafx.scene.control.SelectionMode import javafx.scene.control.TreeItem import javafx.scene.layout.VBox import space.kscience.visionforge.Vision -import space.kscience.visionforge.VisionGroup +import space.kscience.visionforge.solid.SolidGroup import tornadofx.* private fun toTreeItem(vision: Vision, title: String): TreeItem> { return object : TreeItem>(title to vision) { init { - if (vision is VisionGroup) { + if (vision is SolidGroup) { //lazy populate the tree expandedProperty().onChange { expanded -> if (expanded && children.isEmpty()) { - children.setAll(vision.children.map { + children.setAll(vision.items.map { toTreeItem(it.value, it.key.toString()) }) } @@ -24,7 +24,7 @@ private fun toTreeItem(vision: Vision, title: String): TreeItem referenceFactory(obj, binding) + is SolidReference -> referenceFactory(obj, binding) is SolidGroup -> { - Group(obj.children.mapNotNull { (token, obj) -> + Group(obj.items.mapNotNull { (token, obj) -> (obj as? Solid)?.let { logger.info { token.toString() } buildNode(it).apply { @@ -77,7 +77,7 @@ public class FX3DPlugin : AbstractPlugin() { is PolyLine -> PolyLine3D( obj.points.map { Point3D(it.x, it.y, it.z) }, obj.thickness.toFloat(), - obj.computePropertyNode(SolidMaterial.MATERIAL_COLOR_KEY)?.color() + obj.getProperty(SolidMaterial.MATERIAL_COLOR_KEY).color() ).apply { this.meshView.cullFace = CullFace.FRONT } diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXReferenceFactory.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXReferenceFactory.kt index af00f7c5..60b95f9f 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXReferenceFactory.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXReferenceFactory.kt @@ -8,20 +8,21 @@ import space.kscience.dataforge.names.firstOrNull import space.kscience.dataforge.names.isEmpty import space.kscience.visionforge.Vision import space.kscience.visionforge.onPropertyChange +import space.kscience.visionforge.solid.SolidReference.Companion.REFERENCE_CHILD_PROPERTY_PREFIX import kotlin.reflect.KClass -public class FXReferenceFactory(public val plugin: FX3DPlugin) : FX3DFactory { - override val type: KClass get() = SolidReferenceGroup::class +public class FXReferenceFactory(public val plugin: FX3DPlugin) : FX3DFactory { + override val type: KClass get() = SolidReference::class - override fun invoke(obj: SolidReferenceGroup, binding: VisualObjectFXBinding): Node { + override fun invoke(obj: SolidReference, binding: VisualObjectFXBinding): Node { val prototype = obj.prototype val node = plugin.buildNode(prototype) obj.onPropertyChange { name-> - if (name.firstOrNull()?.body == SolidReferenceGroup.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 propertyName = name.cutFirst() - val referenceChild = obj[childName] ?: error("Reference child with name '$childName' not found") + val referenceChild = obj.children[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) } diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/VisualObjectFXBinding.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/VisualObjectFXBinding.kt index 607913d7..680033d4 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/VisualObjectFXBinding.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/VisualObjectFXBinding.kt @@ -7,7 +7,7 @@ import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.startsWith import space.kscience.dataforge.values.Value import space.kscience.visionforge.Vision -import space.kscience.visionforge.computePropertyNode +import space.kscience.visionforge.getProperty import space.kscience.visionforge.onPropertyChange import tornadofx.* @@ -36,7 +36,7 @@ public class VisualObjectFXBinding(public val fx: FX3DPlugin, public val obj: Vi public operator fun get(key: Name): ObjectBinding { return bindings.getOrPut(key) { object : ObjectBinding() { - override fun computeValue(): Meta? = obj.computePropertyNode(key) + override fun computeValue(): Meta = obj.getProperty(key) } } } diff --git a/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/GdmlLoaderOptions.kt b/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/GdmlLoaderOptions.kt index 442cde3c..976db192 100644 --- a/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/GdmlLoaderOptions.kt +++ b/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/GdmlLoaderOptions.kt @@ -6,7 +6,7 @@ import space.kscience.dataforge.names.Name import space.kscience.gdml.* import space.kscience.visionforge.solid.Solid import space.kscience.visionforge.solid.SolidMaterial -import space.kscience.visionforge.solid.invoke +import space.kscience.visionforge.solid.set import space.kscience.visionforge.useStyle import kotlin.random.Random @@ -44,7 +44,7 @@ public class GdmlLoaderOptions { * Configure paint for given solid with given [GdmlMaterial] */ public var configurePaint: SolidMaterial.(material: GdmlMaterial, solid: GdmlSolid) -> Unit = - { material, _ -> color(randomColor(material)) } + { material, _ -> color.set(randomColor(material)) } private set public fun paint(block: SolidMaterial.(material: GdmlMaterial, solid: GdmlSolid) -> Unit) { diff --git a/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/gdmlLoader.kt b/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/gdmlLoader.kt index f80a5511..34a9afa2 100644 --- a/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/gdmlLoader.kt +++ b/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/gdmlLoader.kt @@ -30,10 +30,10 @@ private class GdmlLoader(val settings: GdmlLoaderOptions) { private val proto = SolidGroup() private val solids = proto.group(solidsName) { - setPropertyNode("edges.enabled", false) + setPropertyValue("edges.enabled", false) } - private val referenceStore = HashMap>() + private val referenceStore = HashMap>() fun Solid.configureSolid(root: Gdml, parent: GdmlVolume, solid: GdmlSolid) { val material = parent.materialref.resolve(root) ?: GdmlElement(parent.materialref.ref) @@ -44,7 +44,7 @@ private class GdmlLoader(val settings: GdmlLoaderOptions) { } } - private fun proxySolid(root: Gdml, group: SolidGroup, solid: GdmlSolid, name: String): SolidReferenceGroup { + private fun proxySolid(root: Gdml, group: SolidGroup, solid: GdmlSolid, name: String): SolidReference { val templateName = solidsName + name if (proto[templateName] == null) { solids.addSolid(root, solid, name) @@ -59,7 +59,7 @@ private class GdmlLoader(val settings: GdmlLoaderOptions) { group: SolidGroup, physVolume: GdmlPhysVolume, volume: GdmlGroup, - ): SolidReferenceGroup { + ): SolidReference { val templateName = volumesName + volume.name.asName() if (proto[templateName] == null) { proto[templateName] = volume(root, volume) @@ -321,7 +321,7 @@ private class GdmlLoader(val settings: GdmlLoaderOptions) { ?: error("Volume with ref ${divisionVolume.volumeref.ref} could not be resolved") //TODO add divisions - set(null, volume(root, volume)) + children.static(volume(root, volume)) } private fun volume( @@ -355,7 +355,7 @@ private class GdmlLoader(val settings: GdmlLoaderOptions) { final.useStyle(rootStyle) final.prototypes { - proto.children.forEach { (token, item) -> + proto.items.forEach { (token, item) -> item.parent = null set(token.asName(), item as? Solid) } @@ -383,9 +383,9 @@ public fun Gdml.toVision(block: GdmlLoaderOptions.() -> Unit = {}): SolidGroup { * Append Gdml node to the group */ public fun SolidGroup.gdml(gdml: Gdml, key: String? = null, transformer: GdmlLoaderOptions.() -> Unit = {}) { - val visual = gdml.toVision(transformer) + val vision = gdml.toVision(transformer) //println(Visual3DPlugin.json.stringify(VisualGroup3D.serializer(), visual)) - set(key, visual) + children[key] = vision } @VisionBuilder diff --git a/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/markLayers.kt b/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/markLayers.kt index befc69bb..afc6c99a 100644 --- a/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/markLayers.kt +++ b/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/markLayers.kt @@ -6,10 +6,9 @@ import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.NameToken import space.kscience.dataforge.names.length import space.kscience.dataforge.names.plus -import space.kscience.visionforge.VisionGroup import space.kscience.visionforge.solid.Solid import space.kscience.visionforge.solid.SolidGroup -import space.kscience.visionforge.solid.SolidReferenceGroup +import space.kscience.visionforge.solid.SolidReference import space.kscience.visionforge.solid.layer @@ -23,15 +22,15 @@ private class VisionCounterTree( var selfCount = 1 val children: Map by lazy { - (vision as? VisionGroup)?.children?.mapValues { (key, vision) -> - if (vision is SolidReferenceGroup) { - prototypes.getOrPut(vision.refName) { - VisionCounterTree(vision.refName, vision.prototype, prototypes) + (vision as? SolidGroup)?.items?.mapValues { (key, vision) -> + if (vision is SolidReference) { + prototypes.getOrPut(vision.prototypeName) { + VisionCounterTree(vision.prototypeName, vision.prototype, prototypes) }.apply { selfCount += 1 } } else { - VisionCounterTree(name + key, vision as Solid, prototypes) + VisionCounterTree(name + key, vision, prototypes) } } ?: emptyMap() } @@ -51,10 +50,10 @@ private fun VisionCounterTree.topToBottom(): Sequence = seque } public fun SolidGroup.markLayers(thresholds: List = listOf(500, 1000, 20000, 50000)) { - val logger = manager?.context?.logger + val logger = manager.context.logger val counterTree = VisionCounterTree(Name.EMPTY, this, hashMapOf()) val totalCount = counterTree.childrenCount - if (totalCount > thresholds.firstOrNull() ?: 0) { + if (totalCount > (thresholds.firstOrNull() ?: 0)) { val allNodes = counterTree.topToBottom().distinct().toMutableList() //println("tree construction finished") allNodes.sortWith( diff --git a/visionforge-gdml/src/commonTest/kotlin/TestCubes.kt b/visionforge-gdml/src/commonTest/kotlin/TestCubes.kt index 0ca76fd1..948ad214 100644 --- a/visionforge-gdml/src/commonTest/kotlin/TestCubes.kt +++ b/visionforge-gdml/src/commonTest/kotlin/TestCubes.kt @@ -21,12 +21,12 @@ class TestCubes { @Test fun testCubesDirect() { - val vision = cubes.toVision() + val vision: SolidGroup = cubes.toVision() // println(Solids.encodeToString(vision)) val smallBoxPrototype = vision.getPrototype(Name.parse("solids.smallBox")) as? Box assertNotNull(smallBoxPrototype) assertEquals(30.0, smallBoxPrototype.xSize.toDouble()) - val smallBoxVision = vision["composite-111.smallBox"]?.unref as? Box + val smallBoxVision = vision.children["composite-111.smallBox"]?.unref as? Box assertNotNull(smallBoxVision) assertEquals(30.0, smallBoxVision.xSize.toDouble()) } @@ -55,7 +55,7 @@ class TestCubes { assertNotNull(this.prototype) } if (this is SolidGroup) { - children.forEach { + items.forEach { it.value.checkPrototypes() } } diff --git a/visionforge-markdown/src/commonMain/kotlin/space/kscience/visionforge/markup/VisionOfMarkup.kt b/visionforge-markdown/src/commonMain/kotlin/space/kscience/visionforge/markup/VisionOfMarkup.kt index 87682978..93e7359e 100644 --- a/visionforge-markdown/src/commonMain/kotlin/space/kscience/visionforge/markup/VisionOfMarkup.kt +++ b/visionforge-markdown/src/commonMain/kotlin/space/kscience/visionforge/markup/VisionOfMarkup.kt @@ -9,17 +9,18 @@ import space.kscience.dataforge.meta.string import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.asName import space.kscience.visionforge.Vision -import space.kscience.visionforge.VisionBase +import space.kscience.visionforge.VisionGroup +import space.kscience.visionforge.properties @Serializable @SerialName("vision.markup") public class VisionOfMarkup( public val format: String = COMMONMARK_FORMAT -) : VisionBase() { +) : VisionGroup() { //TODO add templates - public var content: String? by meta.string(CONTENT_PROPERTY_KEY) + public var content: String? by properties().string(CONTENT_PROPERTY_KEY) public companion object { public val CONTENT_PROPERTY_KEY: Name = "content".asName() diff --git a/visionforge-plotly/src/commonMain/kotlin/space/kscience/visionforge/plotly/VisionOfPlotly.kt b/visionforge-plotly/src/commonMain/kotlin/space/kscience/visionforge/plotly/VisionOfPlotly.kt index ebb4773c..880a1961 100644 --- a/visionforge-plotly/src/commonMain/kotlin/space/kscience/visionforge/plotly/VisionOfPlotly.kt +++ b/visionforge-plotly/src/commonMain/kotlin/space/kscience/visionforge/plotly/VisionOfPlotly.kt @@ -2,21 +2,24 @@ package space.kscience.visionforge.plotly import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +import space.kscience.dataforge.meta.asObservable import space.kscience.dataforge.misc.DFExperimental +import space.kscience.dataforge.names.Name import space.kscience.plotly.Plot import space.kscience.plotly.Plotly -import space.kscience.visionforge.VisionBase +import space.kscience.visionforge.VisionGroup +import space.kscience.visionforge.getProperty import space.kscience.visionforge.html.VisionOutput @Serializable @SerialName("vision.plotly") -public class VisionOfPlotly private constructor() : VisionBase() { +public class VisionOfPlotly private constructor() : VisionGroup() { public constructor(plot: Plot) : this() { - properties = plot.meta + setProperty(Name.EMPTY, plot.meta) } - public val plot: Plot get() = Plot(meta) + public val plot: Plot get() = Plot(getProperty(Name.EMPTY).asObservable()) } public fun Plot.asVision(): VisionOfPlotly = VisionOfPlotly(this) diff --git a/visionforge-server/src/main/kotlin/space/kscience/visionforge/server/VisionServer.kt b/visionforge-server/src/main/kotlin/space/kscience/visionforge/server/VisionServer.kt index 7d55771c..bb88fa22 100644 --- a/visionforge-server/src/main/kotlin/space/kscience/visionforge/server/VisionServer.kt +++ b/visionforge-server/src/main/kotlin/space/kscience/visionforge/server/VisionServer.kt @@ -8,7 +8,7 @@ import io.ktor.server.engine.embeddedServer import io.ktor.server.html.respondHtml import io.ktor.server.http.content.resources import io.ktor.server.http.content.static -import io.ktor.server.plugins.cors.CORS +import io.ktor.server.plugins.cors.routing.CORS import io.ktor.server.response.respond import io.ktor.server.response.respondText import io.ktor.server.routing.* @@ -18,7 +18,6 @@ import io.ktor.server.websocket.webSocket import io.ktor.websocket.Frame import kotlinx.coroutines.channels.consumeEach import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import kotlinx.coroutines.withContext diff --git a/visionforge-solid/api/visionforge-solid.api b/visionforge-solid/api/visionforge-solid.api index 34c11260..c763e3c8 100644 --- a/visionforge-solid/api/visionforge-solid.api +++ b/visionforge-solid/api/visionforge-solid.api @@ -717,7 +717,7 @@ public final class space/kscience/visionforge/solid/SolidMaterialKt { } public abstract interface class space/kscience/visionforge/solid/SolidReference : space/kscience/visionforge/VisionGroup { - public fun getPropertyValue (Lspace/kscience/dataforge/names/Name;ZZZ)Lspace/kscience/dataforge/values/Value; + public fun getProperty (Lspace/kscience/dataforge/names/Name;ZZZ)Lspace/kscience/dataforge/values/Value; public abstract fun getPrototype ()Lspace/kscience/visionforge/solid/Solid; } @@ -728,7 +728,7 @@ public final class space/kscience/visionforge/solid/SolidReferenceGroup : space/ public fun (Lspace/kscience/dataforge/names/Name;)V public fun getChildren ()Ljava/util/Map; public fun getDescriptor ()Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor; - public fun getPropertyValue (Lspace/kscience/dataforge/names/Name;ZZZ)Lspace/kscience/dataforge/values/Value; + public fun getProperty (Lspace/kscience/dataforge/names/Name;ZZZ)Lspace/kscience/dataforge/values/Value; public fun getPrototype ()Lspace/kscience/visionforge/solid/Solid; public final fun getRefName ()Lspace/kscience/dataforge/names/Name; public static final fun write$Self (Lspace/kscience/visionforge/solid/SolidReferenceGroup;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/ColorAccessor.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/ColorAccessor.kt index 0f9abc4d..39b7e7b0 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/ColorAccessor.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/ColorAccessor.kt @@ -1,12 +1,13 @@ package space.kscience.visionforge.solid -import space.kscience.dataforge.meta.Configurable import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.plus import space.kscience.dataforge.values.* import space.kscience.visionforge.Colors +import space.kscience.visionforge.Vision import space.kscience.visionforge.VisionBuilder +import space.kscience.visionforge.getProperty import kotlin.properties.ReadOnlyProperty @VisionBuilder @@ -27,8 +28,8 @@ public class ColorAccessor( } } -public fun Configurable.color(): ReadOnlyProperty = ReadOnlyProperty { _, property -> - ColorAccessor(meta, property.name.asName()) +public fun Vision.color(): ReadOnlyProperty = ReadOnlyProperty { _, property -> + ColorAccessor(getProperty(Name.EMPTY), property.name.asName()) } public var ColorAccessor?.string: String? @@ -40,21 +41,21 @@ public var ColorAccessor?.string: String? /** * Set [webcolor](https://en.wikipedia.org/wiki/Web_colors) as string */ -public operator fun ColorAccessor?.invoke(webColor: String) { +public fun ColorAccessor?.set(webColor: String) { this?.value = webColor.asValue() } /** * Set color as RGB integer */ -public operator fun ColorAccessor?.invoke(rgb: Int) { +public fun ColorAccessor?.set(rgb: Int) { this?.value = Colors.rgbToString(rgb).asValue() } /** * Set color as RGB */ -public operator fun ColorAccessor?.invoke(r: UByte, g: UByte, b: UByte) { +public fun ColorAccessor?.set(r: UByte, g: UByte, b: UByte) { this?.value = Colors.rgbToString(r, g, b).asValue() } diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Composite.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Composite.kt index d63e08b6..d300b06b 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Composite.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Composite.kt @@ -3,11 +3,8 @@ package space.kscience.visionforge.solid import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import space.kscience.dataforge.meta.isEmpty -import space.kscience.dataforge.meta.update -import space.kscience.visionforge.VisionBuilder -import space.kscience.visionforge.VisionContainerBuilder -import space.kscience.visionforge.VisionPropertyContainer -import space.kscience.visionforge.set +import space.kscience.dataforge.names.Name +import space.kscience.visionforge.* public enum class CompositeType { GROUP, // Dumb sum of meshes @@ -28,16 +25,16 @@ public class Composite( public inline fun VisionContainerBuilder.composite( type: CompositeType, name: String? = null, - builder: SolidGroup.() -> Unit, + @VisionBuilder builder: SolidGroup.() -> Unit, ): Composite { - val group = SolidGroup().apply(builder) - val children = group.children.values.filterIsInstance() - if (children.size != 2){ + val group = SolidGroup(builder) + val children = group.items.values.toList() + if (children.size != 2) { error("Composite requires exactly two children, but found ${children.size}") } val res = Composite(type, children[0], children[1]) - res.meta.update(group.meta) + res.setProperty(Name.EMPTY, group.getProperty(Name.EMPTY)) set(name, res) return res @@ -50,34 +47,34 @@ public inline fun VisionContainerBuilder.composite( public fun SolidGroup.smartComposite( type: CompositeType, name: String? = null, - builder: SolidGroup.() -> Unit, + @VisionBuilder builder: SolidGroup.() -> Unit, ): Solid = if (type == CompositeType.GROUP) { val group = SolidGroup(builder) if (name == null && group.meta.isEmpty()) { //append directly to group if no properties are defined - group.children.forEach { (_, value) -> + group.items.forEach { (_, value) -> value.parent = null - set(null, value) + children.static(value) } this } else { - set(name, group) + children[name] = group group } } else { - composite(type, name, builder) + children.composite(type, name, builder) } @VisionBuilder public inline fun VisionContainerBuilder.union( name: String? = null, - builder: SolidGroup.() -> Unit + builder: SolidGroup.() -> Unit, ): Composite = composite(CompositeType.UNION, name, builder = builder) @VisionBuilder public inline fun VisionContainerBuilder.subtract( name: String? = null, - builder: SolidGroup.() -> Unit + builder: SolidGroup.() -> Unit, ): Composite = composite(CompositeType.SUBTRACT, name, builder = builder) @VisionBuilder diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Extruded.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Extruded.kt index 30ecb575..6e5a8bb7 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Extruded.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Extruded.kt @@ -4,7 +4,7 @@ import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import space.kscience.dataforge.meta.MutableMeta import space.kscience.dataforge.meta.ObservableMutableMeta -import space.kscience.dataforge.meta.configure +import space.kscience.dataforge.names.Name import space.kscience.visionforge.* import kotlin.math.PI import kotlin.math.cos @@ -40,7 +40,7 @@ public data class Layer(var x: Float, var y: Float, var z: Float, var scale: Flo @SerialName("solid.extrude") public class Extruded( public val shape: List, - public val layers: List + public val layers: List, ) : SolidBase(), GeometrySolid, VisionPropertyContainer { override fun toGeometry(geometryBuilder: GeometryBuilder) { @@ -67,7 +67,7 @@ public class Extruded( for (i in (1 until layers.size)) { upperLayer = layers[i] for (j in (0 until shape.size - 1)) { - //counter clockwise + //counterclockwise geometryBuilder.face4( lowerLayer[j], lowerLayer[j + 1], @@ -99,7 +99,7 @@ public class ExtrudeBuilder( public var layers: MutableList = ArrayList(), - config: ObservableMutableMeta = MutableMeta() + config: ObservableMutableMeta = MutableMeta(), ) : SimpleVisionPropertyContainer(config) { public fun shape(block: Shape2DBuilder.() -> Unit) { this.shape = Shape2DBuilder().apply(block).build() @@ -110,12 +110,12 @@ public class ExtrudeBuilder( } internal fun build(): Extruded = Extruded(shape, layers).apply { - configure(this@ExtrudeBuilder.meta) + setProperty(Name.EMPTY, getProperty(Name.EMPTY)) } } @VisionBuilder public fun VisionContainerBuilder.extruded( name: String? = null, - action: ExtrudeBuilder.() -> Unit = {} + action: ExtrudeBuilder.() -> Unit = {}, ): Extruded = ExtrudeBuilder().apply(action).build().also { set(name, it) } \ No newline at end of file diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Quaternion.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Quaternion.kt deleted file mode 100644 index 617c7a38..00000000 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Quaternion.kt +++ /dev/null @@ -1,11 +0,0 @@ -package space.kscience.visionforge.solid - -import kotlin.jvm.JvmInline - -@JvmInline -public value class Quaternion(public val values: DoubleArray) - -public operator fun Quaternion.component1(): Double = values[0] -public operator fun Quaternion.component2(): Double = values[1] -public operator fun Quaternion.component3(): Double = values[2] -public operator fun Quaternion.component4(): Double = values[3] \ No newline at end of file diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Solid.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Solid.kt index 2b9c883b..c3111d4e 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Solid.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Solid.kt @@ -6,16 +6,12 @@ import space.kscience.dataforge.meta.descriptors.node import space.kscience.dataforge.meta.descriptors.value import space.kscience.dataforge.meta.float import space.kscience.dataforge.meta.get -import space.kscience.dataforge.meta.number import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.plus import space.kscience.dataforge.values.* -import space.kscience.visionforge.Vision +import space.kscience.visionforge.* import space.kscience.visionforge.Vision.Companion.VISIBLE_KEY -import space.kscience.visionforge.hide -import space.kscience.visionforge.inherited -import space.kscience.visionforge.setProperty import space.kscience.visionforge.solid.Solid.Companion.DETAIL_KEY import space.kscience.visionforge.solid.Solid.Companion.IGNORE_KEY import space.kscience.visionforge.solid.Solid.Companion.LAYER_KEY @@ -38,7 +34,7 @@ import kotlin.properties.ReadWriteProperty import kotlin.reflect.KProperty /** - * Interface for 3-dimensional [Vision] + * Interface for a [Vision] representing a 3D object */ public interface Solid : Vision { @@ -121,7 +117,7 @@ public interface Solid : Vision { public var Solid.layer: Int get() = getPropertyValue(LAYER_KEY, inherit = true)?.int ?: 0 set(value) { - setProperty(LAYER_KEY, value) + setPropertyValue(LAYER_KEY, value) } // Common properties @@ -140,23 +136,23 @@ public enum class RotationOrder { */ public var Solid.rotationOrder: RotationOrder get() = getPropertyValue(Solid.ROTATION_ORDER_KEY)?.enum() ?: RotationOrder.XYZ - set(value) = meta.setValue(Solid.ROTATION_ORDER_KEY, value.name.asValue()) + set(value) = setPropertyValue(Solid.ROTATION_ORDER_KEY, value.name.asValue()) /** * Preferred number of polygons for displaying the object. If not defined, uses shape or renderer default. Not inherited */ public var Solid.detail: Int? - get() = getPropertyValue(DETAIL_KEY, false)?.int - set(value) = meta.setValue(DETAIL_KEY, value?.asValue()) + get() = getPropertyValue(DETAIL_KEY, inherit = false)?.int + set(value) = setPropertyValue(DETAIL_KEY, value?.asValue()) /** * If this property is true, the object will be ignored on render. * Property is not inherited. */ public var Vision.ignore: Boolean? - get() = getPropertyValue(IGNORE_KEY, false)?.boolean - set(value) = meta.setValue(IGNORE_KEY, value?.asValue()) + get() = getPropertyValue(IGNORE_KEY, inherit = false)?.boolean + set(value) = setPropertyValue(IGNORE_KEY, value?.asValue()) //var VisualObject.selected: Boolean? // get() = getProperty(SELECTED_KEY).boolean @@ -165,18 +161,18 @@ public var Vision.ignore: Boolean? internal fun float(name: Name, default: Number): ReadWriteProperty = object : ReadWriteProperty { override fun getValue(thisRef: Solid, property: KProperty<*>): Number { - return thisRef.meta.getMeta(name)?.number ?: default + return thisRef.getPropertyValue(name)?.number ?: default } override fun setValue(thisRef: Solid, property: KProperty<*>, value: Number) { - thisRef.setProperty(name, value) + thisRef.setPropertyValue(name, value) } } internal fun point(name: Name, default: Float): ReadWriteProperty = object : ReadWriteProperty { override fun getValue(thisRef: Solid, property: KProperty<*>): Point3D? { - val item = thisRef.meta.getMeta(name) ?: return null + val item = thisRef.meta[name] ?: return null return object : Point3D { override val x: Float get() = item[X_KEY]?.float ?: default override val y: Float get() = item[Y_KEY]?.float ?: default @@ -186,11 +182,11 @@ internal fun point(name: Name, default: Float): ReadWriteProperty, value: Point3D?) { if (value == null) { - thisRef.meta.setMeta(name, null) + thisRef.setProperty(name, null) } else { - thisRef.setProperty(name + X_KEY, value.x) - thisRef.setProperty(name + Y_KEY, value.y) - thisRef.setProperty(name + Z_KEY, value.z) + thisRef.setPropertyValue(name + X_KEY, value.x) + thisRef.setPropertyValue(name + Y_KEY, value.y) + thisRef.setPropertyValue(name + Z_KEY, value.z) } } } diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidBase.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidBase.kt index 70e2501e..6d4b2faf 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidBase.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidBase.kt @@ -2,11 +2,24 @@ package space.kscience.visionforge.solid import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +import space.kscience.dataforge.meta.MutableMeta import space.kscience.dataforge.meta.descriptors.MetaDescriptor -import space.kscience.visionforge.VisionBase +import space.kscience.dataforge.names.Name +import space.kscience.visionforge.AbstractVision +import space.kscience.visionforge.VisionChildren @Serializable @SerialName("solid") -public open class SolidBase : VisionBase(), Solid { +public open class SolidBase : AbstractVision(), Solid { override val descriptor: MetaDescriptor get() = Solid.descriptor + override val children: VisionChildren get() = VisionChildren.empty(this) + + override fun getProperty( + name: Name, + inherit: Boolean, + includeStyles: Boolean, + includeDefaults: Boolean, + ): MutableMeta { + return super.getProperty(name, inherit, includeStyles, includeDefaults) + } } diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidGroup.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidGroup.kt index 76d0708b..20528056 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidGroup.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidGroup.kt @@ -7,6 +7,7 @@ import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.NameToken import space.kscience.visionforge.* + /** * A container with prototype support */ @@ -23,20 +24,26 @@ public interface PrototypeHolder { public fun getPrototype(name: Name): Solid? } + /** - * Represents 3-dimensional Visual Group - * @param prototypes A container for templates visible inside this group + * A [Solid] group with additional accessor methods */ @Serializable @SerialName("group.solid") -public class SolidGroup : VisionGroupBase(), Solid, PrototypeHolder { +public class SolidGroup : VisionGroup(), Solid, PrototypeHolder, MutableVisionGroup, VisionContainerBuilder { - override val children: Map get() = super.childrenInternal.filter { it.key != PROTOTYPES_TOKEN } + public val items: Map + get() = children.keys.mapNotNull { + val value = children[it] as? Solid ?: return@mapNotNull null + it to value + }.toMap() - private var prototypes: MutableVisionGroup? - get() = childrenInternal[PROTOTYPES_TOKEN] as? MutableVisionGroup + public operator fun get(name: Name): Solid? = children[name] as? Solid + + private var prototypes: SolidGroup? + get() = items[PROTOTYPES_TOKEN] as? SolidGroup set(value) { - set(PROTOTYPES_TOKEN, value) + children[PROTOTYPES_TOKEN] = value } @@ -53,36 +60,38 @@ public class SolidGroup : VisionGroupBase(), Solid, PrototypeHolder { * Create or edit prototype node as a group */ override fun prototypes(builder: VisionContainerBuilder.() -> Unit): Unit { - (prototypes ?: SolidGroup().also { - prototypes = it - }).run(builder) + (prototypes ?: SolidGroup().also { prototypes = it }).children.run(builder) } override fun createGroup(): SolidGroup = SolidGroup() - // // override fun update(change: VisionChange) { // updatePosition(change.properties) // super.update(change) // } + override fun set(name: Name?, child: Solid?) { + children[name] = child + } + public companion object { public val PROTOTYPES_TOKEN: NameToken = NameToken("@prototypes") } } -@Suppress("FunctionName") -public fun SolidGroup(block: SolidGroup.() -> Unit): SolidGroup = SolidGroup().apply(block) +public inline fun SolidGroup(block: SolidGroup.() -> Unit): SolidGroup = SolidGroup().apply(block) @VisionBuilder -public fun VisionContainerBuilder.group( +public fun VisionContainerBuilder.group( name: Name? = null, builder: SolidGroup.() -> Unit = {}, -): SolidGroup = SolidGroup().apply(builder).also { set(name, it) } +): SolidGroup = SolidGroup(builder).also { set(name, it) } /** * Define a group with given [name], attach it to this parent and return it. */ @VisionBuilder -public fun VisionContainerBuilder.group(name: String, action: SolidGroup.() -> Unit = {}): SolidGroup = - SolidGroup().apply(action).also { set(name, it) } +public fun VisionContainerBuilder.group( + name: String, + action: SolidGroup.() -> Unit = {}, +): SolidGroup = SolidGroup(action).also { set(name, it) } diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidMaterial.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidMaterial.kt index 75d1f5a3..c68af09c 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidMaterial.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidMaterial.kt @@ -9,6 +9,7 @@ import space.kscience.dataforge.names.plus import space.kscience.dataforge.values.ValueType import space.kscience.dataforge.values.asValue import space.kscience.dataforge.values.number +import space.kscience.dataforge.values.set import space.kscience.visionforge.* import space.kscience.visionforge.solid.SolidMaterial.Companion.MATERIAL_COLOR_KEY import space.kscience.visionforge.solid.SolidMaterial.Companion.MATERIAL_KEY @@ -101,19 +102,19 @@ public class SolidMaterial : Scheme() { } public val Solid.color: ColorAccessor - get() = ColorAccessor(computePropertyValues(), MATERIAL_COLOR_KEY) + get() = ColorAccessor(getProperty(Name.EMPTY), MATERIAL_COLOR_KEY) public var Solid.material: SolidMaterial? - get() = computePropertyNode(MATERIAL_KEY)?.let { SolidMaterial.read(it) } - set(value) = meta.setMeta(MATERIAL_KEY, value?.meta) + get() = SolidMaterial.read(getProperty(MATERIAL_KEY)) + set(value) = setProperty(MATERIAL_KEY, value?.meta) @VisionBuilder public fun Solid.material(builder: SolidMaterial.() -> Unit) { - meta.getOrCreate(MATERIAL_KEY).updateWith(SolidMaterial, builder) + getProperty(MATERIAL_KEY).updateWith(SolidMaterial, builder) } public var Solid.opacity: Number? get() = getPropertyValue(MATERIAL_OPACITY_KEY, inherit = true)?.number set(value) { - meta.setValue(MATERIAL_OPACITY_KEY, value?.asValue()) + setPropertyValue(MATERIAL_OPACITY_KEY, value?.asValue()) } \ No newline at end of file diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidReference.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidReference.kt index 04c54907..8e6e6f59 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidReference.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidReference.kt @@ -1,38 +1,15 @@ package space.kscience.visionforge.solid +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.emptyFlow import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import space.kscience.dataforge.meta.ObservableMutableMeta -import space.kscience.dataforge.meta.descriptors.MetaDescriptor -import space.kscience.dataforge.meta.get +import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.names.* import space.kscience.dataforge.values.Value import space.kscience.visionforge.* - - -public interface SolidReference : VisionGroup { - /** - * The prototype for this reference. - */ - public val prototype: Solid - - override fun getPropertyValue( - name: Name, - inherit: Boolean, - includeStyles: Boolean, - includeDefaults: Boolean - ): Value? { - meta[name]?.value?.let { return it } - if (includeStyles) { - getStyleProperty(name)?.let { return it } - } - prototype.getPropertyValue(name, inherit, includeStyles, includeDefaults)?.let { return it } - if (inherit) { - parent?.getPropertyValue(name, inherit, includeStyles, includeDefaults)?.let { return it } - } - return null - } -} +import space.kscience.visionforge.solid.SolidReference.Companion.REFERENCE_CHILD_PROPERTY_PREFIX /** @@ -46,103 +23,116 @@ public val Vision.unref: Solid else -> error("This Vision is neither Solid nor SolidReference") } -private fun childToken(childName: Name): NameToken = - NameToken(SolidReferenceGroup.REFERENCE_CHILD_PROPERTY_PREFIX, childName.toString()) - -private fun childPropertyName(childName: Name, propertyName: Name): Name = - childToken(childName) + propertyName - /** - * A reference [Solid] to reuse a template object + * @param name A name of reference child relative to prototype root */ -@Serializable -@SerialName("solid.ref") -public class SolidReferenceGroup( - public val refName: Name, -) : VisionBase(), SolidReference, VisionGroup, Solid { +internal class SolidReferenceChild( + val owner: SolidReference, + override var parent: Vision?, + val childName: Name, +) : Solid { - /** - * Recursively search for defined template in the parent - */ - override val prototype: Solid by lazy { - if (parent == null) error("No parent is present for SolidReferenceGroup") - if (parent !is PrototypeHolder) error("Parent does not hold prototypes") - (parent as? PrototypeHolder)?.getPrototype(refName) ?: error("Prototype with name $refName not found") - } + val prototype: Solid + get() = owner.prototype.children[childName] as? Solid + ?: error("Prototype with name $childName not found") - override val children: Map - get() = (prototype as? VisionGroup)?.children - ?.filter { it.key != SolidGroup.PROTOTYPES_TOKEN } - ?.mapValues { - ReferenceChild(this, it.key.asName()) - } ?: emptyMap() + override val meta: Meta get() = owner.getProperty(childToken(childName).asName()) override fun getPropertyValue( name: Name, inherit: Boolean, includeStyles: Boolean, - includeDefaults: Boolean - ): Value? = super.getPropertyValue(name, inherit, includeStyles, includeDefaults) - - override val descriptor: MetaDescriptor get() = prototype.descriptor - - - /** - * A ProxyChild is created temporarily only to interact with properties, it does not store any values - * (properties are stored in external cache) and created and destroyed on-demand). - */ - private class ReferenceChild( - val owner: SolidReferenceGroup, - private val refName: Name - ) : SolidReference, VisionGroup, Solid { - - override val prototype: Solid by lazy { - if (refName.isEmpty()) { - owner.prototype - } else { - val proto = (owner.prototype as? VisionGroup)?.get(refName) - ?: error("Prototype with name $refName not found in SolidReferenceGroup ${owner.refName}") - proto as? Solid ?: error("Prototype with name $refName is ${proto::class} but expected Solid") -// proto.unref as? Solid -// ?: error("Prototype with name $refName is ${proto::class} but expected Solid") - } + includeDefaults: Boolean, + ): Value? { + owner.getPropertyValue( + childPropertyName(childName, name), inherit, includeStyles, includeDefaults + )?.let { return it } + if (includeStyles) { + getStyleProperty(name)?.value?.let { return it } } - - override val meta: ObservableMutableMeta by lazy { - owner.meta.getOrCreate(childToken(refName).asName()) + prototype.getPropertyValue(name, inherit, includeStyles, includeDefaults)?.let { return it } + if (inherit) { + parent?.getPropertyValue(name, inherit, includeStyles, includeDefaults)?.let { return it } } - - override val children: Map - get() = (prototype as? VisionGroup)?.children - ?.filter { it.key != SolidGroup.PROTOTYPES_TOKEN } - ?.mapValues { (key, _) -> - ReferenceChild(owner, refName + key.asName()) - } ?: emptyMap() - - override var parent: VisionGroup? - get() { - val parentName = refName.cutLast() - return if (parentName.isEmpty()) owner else ReferenceChild(owner, parentName) - } - set(_) { - error("Setting a parent for a reference child is not possible") - } - - override fun invalidateProperty(propertyName: Name) { - owner.invalidateProperty(childPropertyName(refName, propertyName)) - } - - override fun update(change: VisionChange) { - change.properties?.let { - updateProperties(Name.EMPTY, it) - } - } - - override val descriptor: MetaDescriptor get() = prototype.descriptor - + return null } - public companion object { + override fun setProperty(name: Name, node: Meta?) { + owner.setProperty(childPropertyName(childName, name), node) + } + + override fun setPropertyValue(name: Name, value: Value?) { + owner.setPropertyValue(childPropertyName(childName, name), value) + } + + override val propertyChanges: SharedFlow + get() = TODO("Not yet implemented") + + override fun invalidateProperty(propertyName: Name) { + owner.invalidateProperty(childPropertyName(childName, propertyName)) + } + + override fun update(change: VisionChange) { + TODO("Not yet implemented") + } + + + override val children: VisionChildren = object : VisionChildren { + override val parent: Vision get() = this@SolidReferenceChild + + override val keys: Set get() = prototype.children.keys + + override val changes: Flow get() = emptyFlow() + + override fun get(token: NameToken): SolidReferenceChild? { + if (token !in prototype.children.keys) return null + return SolidReferenceChild(this@SolidReferenceChild.owner, this@SolidReferenceChild, childName + token) + } + } + + companion object { + + private fun childToken(childName: Name): NameToken = + NameToken(REFERENCE_CHILD_PROPERTY_PREFIX, childName.toString()) + + private fun childPropertyName(childName: Name, propertyName: Name): Name = + childToken(childName) + propertyName + + } +} + +@Serializable +@SerialName("solid.ref") +public class SolidReference( + @SerialName("prototype") public val prototypeName: Name, +) : SolidBase() { + + /** + * The prototype for this reference. + */ + public val prototype: Solid by lazy { + //Recursively search for defined template in the parent + if (parent == null) error("No parent is present for SolidReference") + if (parent !is PrototypeHolder) error("Parent does not hold prototypes") + (parent as? PrototypeHolder)?.getPrototype(prototypeName) + ?: error("Prototype with name $prototypeName not found") + } + + override val children: VisionChildren + get() = object : VisionChildren { + override val parent: Vision get() = this@SolidReference + + override val keys: Set get() = prototype.children.keys + + override val changes: Flow get() = emptyFlow() + + override fun get(token: NameToken): SolidReferenceChild? { + if (token !in prototype.children.keys) return null + return SolidReferenceChild(this@SolidReference, this@SolidReference, token.asName()) + } + } + + public companion object{ public const val REFERENCE_CHILD_PROPERTY_PREFIX: String = "@child" } } @@ -150,33 +140,123 @@ public class SolidReferenceGroup( /** * Create ref for existing prototype */ -public fun SolidGroup.ref( +public fun VisionContainerBuilder.ref( templateName: Name, name: String? = null, -): SolidReferenceGroup = SolidReferenceGroup(templateName).also { set(name, it) } +): SolidReference = SolidReference(templateName).also { set(name, it) } -public fun SolidGroup.ref( +public fun VisionContainerBuilder.ref( templateName: String, name: String? = null, -): SolidReferenceGroup = ref(Name.parse(templateName), name) +): SolidReference = ref(Name.parse(templateName), name) /** - * Add new [SolidReferenceGroup] wrapping given object and automatically adding it to the prototypes. - * One must ensure that [prototypeHolder] is a parent of this group. + * Add new [SolidReference] wrapping given object and automatically adding it to the prototypes. */ public fun SolidGroup.newRef( name: String?, obj: Solid, - prototypeHolder: PrototypeHolder = this, - templateName: Name = Name.parse(name ?: obj.toString()), -): SolidReferenceGroup { - val existing = getPrototype(templateName) + prototypeHolder: SolidGroup = this, + prototypeName: Name = Name.parse(name ?: obj.toString()), +): SolidReference { + val existing = prototypeHolder.getPrototype(prototypeName) if (existing == null) { prototypeHolder.prototypes { - set(templateName, obj) + set(prototypeName, obj) } } else if (existing != obj) { error("Can't add different prototype on top of existing one") } - return ref(templateName, name) + return children.ref(prototypeName, name) } + + +// +// +///** +// * A reference [Solid] to reuse a template object +// */ +//@Serializable +//@SerialName("solid.ref") +//public class SolidReferenceGroup( +// public val refName: Name, +//) : VisionGroup(), SolidReference, VisionGroup, Solid { +// +// /** +// * Recursively search for defined template in the parent +// */ +// override val prototype: Solid by lazy { +// if (parent == null) error("No parent is present for SolidReferenceGroup") +// if (parent !is PrototypeHolder) error("Parent does not hold prototypes") +// (parent as? PrototypeHolder)?.getPrototype(refName) ?: error("Prototype with name $refName not found") +// } +// +// override val items: Map> +// get() = (prototype as? VisionGroup<*>)?.items +// ?.filter { it.key != SolidGroup.PROTOTYPES_TOKEN } +// ?.mapValues { +// VisionGroupItem.Node(ReferenceChild(this, it.key.asName())) +// } ?: emptyMap() +// +// override val descriptor: MetaDescriptor get() = prototype.descriptor +// +// +// /** +// * A ProxyChild is created temporarily only to interact with properties, it does not store any values +// * (properties are stored in external cache) and created and destroyed on-demand). +// */ +// private class ReferenceChild( +// val owner: SolidReferenceGroup, +// private val refName: Name, +// ) : SolidReference, VisionGroup, Solid { +// +// override val prototype: Solid by lazy { +// if (refName.isEmpty()) { +// owner.prototype +// } else { +// val proto = (owner.prototype).children.get(refName) +// ?: error("Prototype with name $refName not found in SolidReferenceGroup ${owner.refName}") +// proto as? Solid ?: error("Prototype with name $refName is ${proto::class} but expected Solid") +//// proto.unref as? Solid +//// ?: error("Prototype with name $refName is ${proto::class} but expected Solid") +// } +// } +// +// override val meta: ObservableMutableMeta by lazy { +// owner.meta.getOrCreate(childToken(refName).asName()) +// } +// +// override val items: Map> +// get() = (prototype as? VisionGroup<*>)?.items +// ?.filter { it.key != SolidGroup.PROTOTYPES_TOKEN } +// ?.mapValues { (key, _) -> +// VisionGroupItem.Node(ReferenceChild(owner, refName + key.asName())) +// } ?: emptyMap() +// +// override var parent: VisionGroup<*>? +// get() { +// val parentName = refName.cutLast() +// return if (parentName.isEmpty()) owner else ReferenceChild(owner, parentName) +// } +// set(_) { +// error("Setting a parent for a reference child is not possible") +// } +// +// override fun invalidateProperty(propertyName: Name) { +// owner.invalidateProperty(childPropertyName(refName, propertyName)) +// } +// +// override fun update(change: VisionChange) { +// change.properties?.let { +// updateProperties(it, Name.EMPTY) +// } +// } +// +// override val descriptor: MetaDescriptor get() = prototype.descriptor +// +// } +// +// public companion object { +// public const val REFERENCE_CHILD_PROPERTY_PREFIX: String = "@child" +// } +//} diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Solids.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Solids.kt index b43d6475..6e9d00d9 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Solids.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Solids.kt @@ -29,7 +29,7 @@ public class Solids(meta: Meta) : VisionPlugin(meta) { private fun PolymorphicModuleBuilder.solids() { subclass(SolidGroup.serializer()) - subclass(SolidReferenceGroup.serializer()) + subclass(SolidReference.serializer()) subclass(Composite.serializer()) subclass(Box.serializer()) subclass(GenericHexagon.serializer()) @@ -47,8 +47,7 @@ public class Solids(meta: Meta) : VisionPlugin(meta) { public val serializersModuleForSolids: SerializersModule = SerializersModule { polymorphic(Vision::class) { - subclass(VisionBase.serializer()) - subclass(VisionGroupBase.serializer()) + subclass(VisionGroup.serializer()) solids() } diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Sphere.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Sphere.kt index bae8d83d..45305e4a 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Sphere.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Sphere.kt @@ -21,7 +21,7 @@ public class Sphere( ) : SolidBase(), GeometrySolid, VisionPropertyContainer { override fun toGeometry(geometryBuilder: GeometryBuilder) { - fun point3DfromSphCoord(r: Float, theta: Float, phi: Float): Point3D { + fun point3dFromSphCoord(r: Float, theta: Float, phi: Float): Point3D { // This transformation matches three.js sphere implementation val y = r * cos(theta) val z = r * sin(theta) * sin(phi) @@ -39,10 +39,10 @@ public class Sphere( for (j in 0 until segments) { // phi iteration val phi1 = phiStart + j * phiStep val phi2 = phi1 + phiStep - val point1 = point3DfromSphCoord(radius, theta1, phi1) - val point2 = point3DfromSphCoord(radius, theta1, phi2) - val point3 = point3DfromSphCoord(radius, theta2, phi2) - val point4 = point3DfromSphCoord(radius, theta2, phi1) + val point1 = point3dFromSphCoord(radius, theta1, phi1) + val point2 = point3dFromSphCoord(radius, theta1, phi2) + val point3 = point3dFromSphCoord(radius, theta2, phi2) + val point4 = point3dFromSphCoord(radius, theta2, phi1) geometryBuilder.apply { // 1-2-3-4 gives the same face but with opposite orientation face4(point1, point4, point3, point2) diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SphereLayer.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SphereLayer.kt index 05149e2e..b5ae5500 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SphereLayer.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SphereLayer.kt @@ -27,7 +27,7 @@ public class SphereLayer( require(outerRadius > 0) { "Outer radius must be positive" } require(innerRadius >= 0) { "inner radius must be non-negative" } - fun point3DfromSphCoord(r: Float, theta: Float, phi: Float): Point3D { + fun point3dFromSphCoord(r: Float, theta: Float, phi: Float): Point3D { // This transformation matches three.js sphere implementation val y = r * cos(theta) val z = r * sin(theta) * sin(phi) @@ -46,17 +46,17 @@ public class SphereLayer( val phi1 = phiStart + j * phiStep val phi2 = phi1 + phiStep //outer points - val outerPoint1 = point3DfromSphCoord(outerRadius, theta1, phi1) - val outerPoint2 = point3DfromSphCoord(outerRadius, theta1, phi2) - val outerPoint3 = point3DfromSphCoord(outerRadius, theta2, phi2) - val outerPoint4 = point3DfromSphCoord(outerRadius, theta2, phi1) + val outerPoint1 = point3dFromSphCoord(outerRadius, theta1, phi1) + val outerPoint2 = point3dFromSphCoord(outerRadius, theta1, phi2) + val outerPoint3 = point3dFromSphCoord(outerRadius, theta2, phi2) + val outerPoint4 = point3dFromSphCoord(outerRadius, theta2, phi1) // 1-2-3-4 gives the same face but with opposite orientation face4(outerPoint1, outerPoint4, outerPoint3, outerPoint2) if (innerRadius > 0) { - val innerPoint1 = point3DfromSphCoord(innerRadius, theta1, phi1) - val innerPoint2 = point3DfromSphCoord(innerRadius, theta1, phi2) - val innerPoint3 = point3DfromSphCoord(innerRadius, theta2, phi2) - val innerPoint4 = point3DfromSphCoord(innerRadius, theta2, phi1) + val innerPoint1 = point3dFromSphCoord(innerRadius, theta1, phi1) + val innerPoint2 = point3dFromSphCoord(innerRadius, theta1, phi2) + val innerPoint3 = point3dFromSphCoord(innerRadius, theta2, phi2) + val innerPoint4 = point3dFromSphCoord(innerRadius, theta2, phi1) face4(innerPoint1, innerPoint2, innerPoint3, innerPoint4) //the cup if (i == segments - 1 && theta != PI.toFloat() && innerRadius != outerRadius) { diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/geometry.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/geometry.kt index a0ad09a0..e9c9c146 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/geometry.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/geometry.kt @@ -112,9 +112,9 @@ public fun Point3D.toMeta(): Meta = Meta { internal fun Meta.toVector(default: Float = 0f) = Point3D( - this[Solid.X_KEY].float ?: default, - this[Solid.Y_KEY].float ?: default, - this[Solid.Z_KEY].float ?: default + this[X_KEY].float ?: default, + this[Y_KEY].float ?: default, + this[Z_KEY].float ?: default ) //internal fun Solid.updatePosition(meta: Meta?) { diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Canvas3DOptions.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Canvas3DOptions.kt index e74b47f0..aa53d424 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Canvas3DOptions.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Canvas3DOptions.kt @@ -5,6 +5,7 @@ import space.kscience.dataforge.meta.descriptors.MetaDescriptor import space.kscience.dataforge.meta.descriptors.scheme import space.kscience.dataforge.meta.descriptors.value import space.kscience.dataforge.names.Name +import space.kscience.dataforge.values.set import space.kscience.visionforge.hide import space.kscience.visionforge.widgetType diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/transform/RemoveSingleChild.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/transform/RemoveSingleChild.kt index 39aadbf1..bab2e518 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/transform/RemoveSingleChild.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/transform/RemoveSingleChild.kt @@ -1,31 +1,26 @@ package space.kscience.visionforge.solid.transform -import space.kscience.dataforge.meta.configure -import space.kscience.dataforge.meta.update import space.kscience.dataforge.misc.DFExperimental +import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.asName -import space.kscience.visionforge.* +import space.kscience.visionforge.getProperty import space.kscience.visionforge.solid.* private operator fun Number.plus(other: Number) = toFloat() + other.toFloat() private operator fun Number.times(other: Number) = toFloat() * other.toFloat() @DFExperimental -internal fun Vision.updateFrom(other: Vision): Vision { - if (this is Solid && other is Solid) { - x += other.x - y += other.y - z += other.y - rotationX += other.rotationX - rotationY += other.rotationY - rotationZ += other.rotationZ - scaleX *= other.scaleX - scaleY *= other.scaleY - scaleZ *= other.scaleZ - configure{ - update(other.meta) - } - } +internal fun Solid.updateFrom(other: Solid): Solid { + x += other.x + y += other.y + z += other.y + rotationX += other.rotationX + rotationY += other.rotationY + rotationZ += other.rotationZ + scaleX *= other.scaleX + scaleY *= other.scaleY + scaleZ *= other.scaleZ + setProperty(Name.EMPTY, other.getProperty(Name.EMPTY)) return this } @@ -34,17 +29,17 @@ internal fun Vision.updateFrom(other: Vision): Vision { internal object RemoveSingleChild : VisualTreeTransform() { override fun SolidGroup.transformInPlace() { - fun MutableVisionGroup.replaceChildren() { - children.forEach { (childName, parent) -> - if (parent is SolidReferenceGroup) return@forEach //ignore refs - if (parent is MutableVisionGroup) { + fun SolidGroup.replaceChildren() { + items.forEach { (childName, parent) -> + if (parent is SolidReference) return@forEach //ignore refs + if (parent is SolidGroup) { parent.replaceChildren() } - if (parent is VisionGroup && parent.children.size == 1) { - val child = parent.children.values.first() + if (parent is SolidGroup && parent.items.size == 1) { + val child: Solid = parent.items.values.first() val newParent = child.updateFrom(parent) newParent.parent = null - set(childName.asName(), newParent) + children[childName.asName()] = newParent } } } diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/transform/UnRef.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/transform/UnRef.kt index 890c8291..b78863d2 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/transform/UnRef.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/transform/UnRef.kt @@ -2,41 +2,47 @@ package space.kscience.visionforge.solid.transform import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.names.Name -import space.kscience.dataforge.names.asName -import space.kscience.visionforge.MutableVisionGroup -import space.kscience.visionforge.VisionGroup import space.kscience.visionforge.solid.SolidGroup -import space.kscience.visionforge.solid.SolidReferenceGroup +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 internal object UnRef : VisualTreeTransform() { - private fun VisionGroup.countRefs(): Map { - return children.values.fold(HashMap()) { reducer, obj -> - if (obj is VisionGroup) { - val counter = obj.countRefs() + private fun SolidGroup.countRefs(): Map { + return items.values.fold(HashMap()) { reducer, vision -> + if (vision is SolidGroup) { + val counter = vision.countRefs() counter.forEach { (key, value) -> reducer[key] = (reducer[key] ?: 0) + value } - } else if (obj is SolidReferenceGroup) { - reducer[obj.refName] = (reducer[obj.refName] ?: 0) + 1 + } else if (vision is SolidReference) { + reducer[vision.prototypeName] = (reducer[vision.prototypeName] ?: 0) + 1 } return reducer } } - private fun MutableVisionGroup.unref(name: Name) { + private fun SolidGroup.unref(name: Name) { (this as? SolidGroup)?.prototypes{ set(name, null) } - children.filter { (it.value as? SolidReferenceGroup)?.refName == name }.forEach { (key, value) -> - val reference = value as SolidReferenceGroup + items.filter { (it.value as? SolidReference)?.prototypeName == name }.forEach { (key, value) -> + val reference = value as SolidReference val newChild = reference.prototype.updateFrom(reference) newChild.parent = null - set(key.asName(), newChild) // replace proxy with merged object + children[key] = newChild // replace proxy with merged object } - children.values.filterIsInstance().forEach { it.unref(name) } + items.values.filterIsInstance().forEach { it.unref(name) } } override fun SolidGroup.transformInPlace() { diff --git a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/CompositeTest.kt b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/CompositeTest.kt index 411aa75b..17dfb925 100644 --- a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/CompositeTest.kt +++ b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/CompositeTest.kt @@ -18,7 +18,7 @@ class CompositeTest { detail = 32 } material { - color("pink") + color.set("pink") } } } diff --git a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/ConvexTest.kt b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/ConvexTest.kt index a7133006..500bddb2 100644 --- a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/ConvexTest.kt +++ b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/ConvexTest.kt @@ -1,7 +1,6 @@ package space.kscience.visionforge.solid import space.kscience.dataforge.meta.getIndexed -import space.kscience.dataforge.meta.node import space.kscience.dataforge.meta.toMeta import space.kscience.dataforge.misc.DFExperimental import kotlin.test.Test @@ -12,7 +11,7 @@ class ConvexTest { @Suppress("UNUSED_VARIABLE") @Test fun testConvexBuilder() { - val group = SolidGroup().apply { + val group = SolidGroup{ convex { point(50, 50, -50) point(50, -50, -50) @@ -25,7 +24,7 @@ class ConvexTest { } } - val convex = group.children.values.first() as Convex + val convex = group.items.values.first() as Convex val json = Solids.jsonForSolids.encodeToJsonElement(Convex.serializer(), convex) val meta = json.toMeta() diff --git a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/GroupTest.kt b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/GroupTest.kt index 4e78e340..87de5c77 100644 --- a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/GroupTest.kt +++ b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/GroupTest.kt @@ -9,7 +9,7 @@ import kotlin.test.assertEquals class GroupTest { @Test fun testGroupWithComposite() { - val group = SolidGroup().apply { + val group = SolidGroup{ union("union") { box(100, 100, 100) { z = 100 @@ -18,7 +18,7 @@ class GroupTest { } box(100, 100, 100) material { - color(Colors.lightgreen) + color.set(Colors.lightgreen) opacity = 0.3f } } @@ -30,7 +30,7 @@ class GroupTest { } box(100, 100, 100) y = 300 - color(Colors.red) + color.set(Colors.red) } subtract("subtract") { box(100, 100, 100) { @@ -40,12 +40,12 @@ class GroupTest { } box(100, 100, 100) y = -300 - color(Colors.blue) + color.set(Colors.blue) } } - assertEquals(3, group.children.count()) - assertEquals(300.0, (group["intersect"] as Solid).y.toDouble()) - assertEquals(-300.0, (group["subtract"] as Solid).y.toDouble()) + 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()) } } \ No newline at end of file diff --git a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/PropertyTest.kt b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/PropertyTest.kt index ec58e4d2..8e6f0b89 100644 --- a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/PropertyTest.kt +++ b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/PropertyTest.kt @@ -15,7 +15,7 @@ class PropertyTest { val box = Box(10.0f, 10.0f,10.0f) box.material { //meta["color"] = "pink" - color("pink") + color.set("pink") } assertEquals("pink", box.meta["material.color"]?.string) assertEquals("pink", box.color.string) @@ -33,7 +33,7 @@ class PropertyTest { } box.material { - color("pink") + color.set("pink") } assertEquals("pink", c) @@ -43,7 +43,7 @@ class PropertyTest { fun testInheritedProperty() { var box: Box? = null val group = SolidGroup().apply { - setPropertyNode("test", 22) + setPropertyValue("test", 22) group { box = box(100, 100, 100) } @@ -54,14 +54,14 @@ class PropertyTest { @Test fun testStyleProperty() { var box: Box? = null - val group = SolidGroup().apply { + val group = SolidGroup{ styleSheet { - set("testStyle") { + update("testStyle") { "test" put 22 } } group { - box = box(100, 100, 100).apply { + box = box(100, 100, 100) { useStyle("testStyle") } } @@ -74,7 +74,7 @@ class PropertyTest { var box: Box? = null val group = SolidGroup().apply { styleSheet { - set("testStyle") { + update("testStyle") { SolidMaterial.MATERIAL_COLOR_KEY put "#555555" } } @@ -89,10 +89,10 @@ class PropertyTest { @Test fun testReferenceStyleProperty() { - var box: SolidReferenceGroup? = null + var box: SolidReference? = null val group = SolidGroup{ styleSheet { - set("testStyle") { + update("testStyle") { SolidMaterial.MATERIAL_COLOR_KEY put "#555555" } } @@ -105,6 +105,6 @@ class PropertyTest { box = ref("box".asName()) } } - assertEquals("#555555", box?.color.string) + assertEquals("#555555", box!!.color.string) } } \ No newline at end of file diff --git a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/SerializationTest.kt b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/SerializationTest.kt index 558d005c..70cb9c4e 100644 --- a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/SerializationTest.kt +++ b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/SerializationTest.kt @@ -2,7 +2,6 @@ package space.kscience.visionforge.solid import space.kscience.dataforge.names.Name import space.kscience.visionforge.Colors -import space.kscience.visionforge.MutableVisionGroup import space.kscience.visionforge.get import kotlin.test.Test import kotlin.test.assertEquals @@ -14,10 +13,10 @@ import kotlin.test.assertEquals fun SolidGroup.refGroup( name: String, templateName: Name = Name.parse(name), - block: MutableVisionGroup.() -> Unit -): SolidReferenceGroup { + block: SolidGroup.() -> Unit +): SolidReference { val group = SolidGroup().apply(block) - return newRef(name, group, templateName = templateName) + return newRef(name, group, prototypeName = templateName) } @@ -25,7 +24,7 @@ class SerializationTest { @Test fun testCubeSerialization() { val cube = Box(100f, 100f, 100f).apply { - color(222) + color.set(222) x = 100 z = -100 } @@ -38,7 +37,7 @@ class SerializationTest { @Test fun testProxySerialization() { val cube = Box(100f, 100f, 100f).apply { - color(222) + color.set(222) x = 100 z = -100 } @@ -53,21 +52,21 @@ class SerializationTest { val string = Solids.encodeToString(group) println(string) val reconstructed = Solids.decodeFromString(string) as SolidGroup - assertEquals(group["cube"]?.meta, reconstructed["cube"]?.meta) + assertEquals(group.children["cube"]?.meta, reconstructed.children["cube"]?.meta) } @Test fun lightSerialization(){ val group = SolidGroup { ambientLight { - color(Colors.white) + color.set(Colors.white) intensity = 100.0 } } val serialized = Solids.encodeToString(group) val reconstructed = Solids.decodeFromString(serialized) as SolidGroup - assertEquals(100.0, (reconstructed["@ambientLight"] as AmbientLightSource).intensity.toDouble()) + assertEquals(100.0, (reconstructed.children["@ambientLight"] as AmbientLightSource).intensity.toDouble()) } } \ No newline at end of file diff --git a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/SolidPluginTest.kt b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/SolidPluginTest.kt index 23a48a63..4ac22e1f 100644 --- a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/SolidPluginTest.kt +++ b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/SolidPluginTest.kt @@ -24,6 +24,9 @@ class SolidPluginTest { val reconstructed = visionManager.decodeFromMeta(meta) as SolidGroup - assertEquals(visionManager.encodeToJsonElement(vision["aBox"]!!), visionManager.encodeToJsonElement(reconstructed["aBox"]!!)) + assertEquals( + visionManager.encodeToJsonElement(vision.children["aBox"]!!), + visionManager.encodeToJsonElement(reconstructed.children["aBox"]!!) + ) } } \ No newline at end of file diff --git a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/SolidReferenceTest.kt b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/SolidReferenceTest.kt index cfdd73f0..4726f246 100644 --- a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/SolidReferenceTest.kt +++ b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/SolidReferenceTest.kt @@ -15,7 +15,7 @@ class SolidReferenceTest { SolidMaterial.MATERIAL_COLOR_KEY put "red" } newRef("test", Box(100f,100f,100f).apply { - color("blue") + color.set("blue") useStyle(theStyle) }) } @@ -23,13 +23,13 @@ class SolidReferenceTest { @Test fun testReferenceProperty(){ - assertEquals("blue", (groupWithReference["test"] as Solid).color.string) + assertEquals("blue", (groupWithReference.children["test"] as Solid).color.string) } @Test fun testReferenceSerialization(){ val serialized = Solids.jsonForSolids.encodeToJsonElement(groupWithReference) val deserialized = Solids.jsonForSolids.decodeFromJsonElement(SolidGroup.serializer(), serialized) - assertEquals("blue", (deserialized["test"] as Solid).color.string) + assertEquals("blue", (deserialized.children["test"] as Solid).color.string) } } \ No newline at end of file diff --git a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/VisionUpdateTest.kt b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/VisionUpdateTest.kt index 26b4b40d..3fbf3ad4 100644 --- a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/VisionUpdateTest.kt +++ b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/VisionUpdateTest.kt @@ -21,24 +21,24 @@ class VisionUpdateTest { box(200,200,200, name = "origin") } val dif = VisionChange{ - group("top") { - color(123) + group ("top") { + color.set(123) box(100,100,100) } propertyChanged("top".asName(), SolidMaterial.MATERIAL_COLOR_KEY, Meta("red".asValue())) propertyChanged("origin".asName(), SolidMaterial.MATERIAL_COLOR_KEY, Meta("red".asValue())) } targetVision.update(dif) - assertTrue { targetVision["top"] is SolidGroup } - assertEquals("red", (targetVision["origin"] as Solid).color.string) // Should work - assertEquals("#00007b", (targetVision["top"] as Solid).color.string) // new item always takes precedence + 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 } @Test fun testVisionChangeSerialization(){ val change = VisionChange{ group("top") { - color(123) + color.set(123) box(100,100,100) } propertyChanged("top".asName(), SolidMaterial.MATERIAL_COLOR_KEY, Meta("red".asValue())) diff --git a/visionforge-tables/src/commonMain/kotlin/space/kscience/visionforge/tables/VisionOfTable.kt b/visionforge-tables/src/commonMain/kotlin/space/kscience/visionforge/tables/VisionOfTable.kt index 6370b699..d76398fc 100644 --- a/visionforge-tables/src/commonMain/kotlin/space/kscience/visionforge/tables/VisionOfTable.kt +++ b/visionforge-tables/src/commonMain/kotlin/space/kscience/visionforge/tables/VisionOfTable.kt @@ -12,8 +12,9 @@ import space.kscience.dataforge.values.Null import space.kscience.dataforge.values.Value import space.kscience.dataforge.values.asValue import space.kscience.tables.* -import space.kscience.visionforge.VisionBase +import space.kscience.visionforge.VisionGroup import space.kscience.visionforge.html.VisionOutput +import space.kscience.visionforge.properties import kotlin.jvm.JvmName import kotlin.reflect.typeOf @@ -41,12 +42,13 @@ public val ColumnHeader.properties: ValueColumnScheme get() = ValueColumn @SerialName("vision.table") public class VisionOfTable( override val headers: List<@Serializable(ColumnHeaderSerializer::class) ColumnHeader>, -) : VisionBase(), Rows { +) : VisionGroup(), Rows { public var data: List get() = meta.getIndexed("rows").entries.sortedBy { it.key?.toInt() }.map { it.value } set(value) { - meta["rows"] = value + //TODO Make it better + properties()["rows"] = value } public val rows: List get() = data.map(::MetaRow) diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/MeshThreeFactory.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/MeshThreeFactory.kt index 7ac4f13e..68b44fc5 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/MeshThreeFactory.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/MeshThreeFactory.kt @@ -4,16 +4,15 @@ import info.laht.threekt.core.BufferGeometry import info.laht.threekt.geometries.EdgesGeometry import info.laht.threekt.objects.LineSegments import info.laht.threekt.objects.Mesh -import space.kscience.dataforge.meta.updateWith +import space.kscience.dataforge.meta.boolean import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.plus import space.kscience.dataforge.names.startsWith -import space.kscience.dataforge.values.boolean import space.kscience.visionforge.VisionBuilder -import space.kscience.visionforge.computePropertyNode +import space.kscience.visionforge.getProperty import space.kscience.visionforge.onPropertyChange -import space.kscience.visionforge.setProperty +import space.kscience.visionforge.setPropertyValue import space.kscience.visionforge.solid.Solid import space.kscience.visionforge.solid.SolidMaterial import space.kscience.visionforge.solid.layer @@ -32,7 +31,7 @@ public abstract class MeshThreeFactory( */ public abstract fun buildGeometry(obj: T): BufferGeometry - override fun invoke(three: ThreePlugin, obj: T): Mesh { + override fun build(three: ThreePlugin, obj: T): Mesh { val geometry = buildGeometry(obj) //val meshMeta: Meta = obj.properties[Material3D.MATERIAL_KEY]?.node ?: Meta.empty @@ -78,8 +77,8 @@ public abstract class MeshThreeFactory( @VisionBuilder public fun Solid.edges(enabled: Boolean = true, block: SolidMaterial.() -> Unit = {}) { - setProperty(EDGES_ENABLED_KEY, enabled) - meta.getOrCreate(EDGES_MATERIAL_KEY).updateWith(SolidMaterial, block) + setPropertyValue(EDGES_ENABLED_KEY, enabled) + SolidMaterial.write(getProperty(EDGES_MATERIAL_KEY)).apply(block) } internal fun Mesh.applyProperties(obj: Solid): Mesh = apply { @@ -95,9 +94,9 @@ internal fun Mesh.applyProperties(obj: Solid): Mesh = apply { public fun Mesh.applyEdges(obj: Solid) { val edges = children.find { it.name == "@edges" } as? LineSegments //inherited edges definition, enabled by default - if (obj.getPropertyValue(EDGES_ENABLED_KEY, inherit = true)?.boolean != false) { + if (obj.getProperty(EDGES_ENABLED_KEY, inherit = true).boolean != false) { val bufferGeometry = geometry as? BufferGeometry ?: return - val material = ThreeMaterials.getLineMaterial(obj.computePropertyNode(EDGES_MATERIAL_KEY), true) + val material = ThreeMaterials.getLineMaterial(obj.getProperty(EDGES_MATERIAL_KEY), true) if (edges == null) { add( LineSegments( diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeAmbientLightFactory.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeAmbientLightFactory.kt index c3d6bfa0..9e105bde 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeAmbientLightFactory.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeAmbientLightFactory.kt @@ -8,7 +8,7 @@ import kotlin.reflect.KClass public object ThreeAmbientLightFactory : ThreeFactory { override val type: KClass get() = AmbientLightSource::class - override fun invoke(three: ThreePlugin, obj: AmbientLightSource): AmbientLight { + override fun build(three: ThreePlugin, obj: AmbientLightSource): AmbientLight { val res = AmbientLight().apply { color = obj.color.threeColor() ?: Color(0x404040) intensity = obj.intensity.toDouble() diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCanvasLabelFactory.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCanvasLabelFactory.kt index 7dd30a34..3fb87c7b 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCanvasLabelFactory.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCanvasLabelFactory.kt @@ -11,6 +11,7 @@ import org.w3c.dom.CanvasRenderingContext2D import org.w3c.dom.CanvasTextBaseline import org.w3c.dom.HTMLCanvasElement import org.w3c.dom.MIDDLE +import space.kscience.visionforge.getProperty import space.kscience.visionforge.solid.SolidLabel import space.kscience.visionforge.solid.SolidMaterial import space.kscience.visionforge.solid.three.ThreeCanvas.Companion.DO_NOT_HIGHLIGHT_TAG @@ -22,11 +23,11 @@ import kotlin.reflect.KClass public object ThreeCanvasLabelFactory : ThreeFactory { override val type: KClass get() = SolidLabel::class - override fun invoke(three: ThreePlugin, obj: SolidLabel): Object3D { + override fun build(three: ThreePlugin, obj: SolidLabel): Object3D { val canvas = document.createElement("canvas") as HTMLCanvasElement val context = canvas.getContext("2d") as CanvasRenderingContext2D context.font = "Bold ${obj.fontSize}pt ${obj.fontFamily}" - context.fillStyle = obj.getPropertyValue(SolidMaterial.MATERIAL_COLOR_KEY)?.value ?: "black" + context.fillStyle = obj.getProperty(SolidMaterial.MATERIAL_COLOR_KEY)?.value ?: "black" context.textBaseline = CanvasTextBaseline.MIDDLE val metrics = context.measureText(obj.text) //canvas.width = metrics.width.toInt() diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCompositeFactory.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCompositeFactory.kt index 74528c50..4bd9826c 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCompositeFactory.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCompositeFactory.kt @@ -37,7 +37,7 @@ public class ThreeCompositeFactory(public val three: ThreePlugin) : ThreeFactory override val type: KClass get() = Composite::class - override fun invoke(three: ThreePlugin, obj: Composite): Mesh { + override fun build(three: ThreePlugin, obj: Composite): Mesh { val first = three.buildObject3D(obj.first).takeIfMesh() ?: error("First part of composite is not a mesh") val second = three.buildObject3D(obj.second).takeIfMesh() ?: error("Second part of composite is not a mesh") return when (obj.compositeType) { diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeFactory.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeFactory.kt index 6e540f71..3397ea96 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeFactory.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeFactory.kt @@ -22,7 +22,7 @@ public interface ThreeFactory { public val type: KClass - public operator fun invoke(three: ThreePlugin, obj: T): Object3D + public fun build(three: ThreePlugin, obj: T): Object3D public companion object { public const val TYPE: String = "threeFactory" diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeLabelFactory.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeLabelFactory.kt index d07f542e..46df4405 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeLabelFactory.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeLabelFactory.kt @@ -17,7 +17,7 @@ import kotlin.reflect.KClass public object ThreeLabelFactory : ThreeFactory { override val type: KClass get() = SolidLabel::class - override fun invoke(three: ThreePlugin, obj: SolidLabel): Object3D { + override fun build(three: ThreePlugin, obj: SolidLabel): Object3D { val textGeo = TextBufferGeometry(obj.text, jso { font = obj.fontFamily size = 20 diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeLineFactory.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeLineFactory.kt index ee36d74b..da1984f3 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeLineFactory.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeLineFactory.kt @@ -4,7 +4,7 @@ import info.laht.threekt.core.BufferGeometry import info.laht.threekt.core.Object3D import info.laht.threekt.math.Color import info.laht.threekt.objects.LineSegments -import space.kscience.visionforge.computePropertyNode +import space.kscience.visionforge.getProperty import space.kscience.visionforge.onPropertyChange import space.kscience.visionforge.solid.PolyLine import space.kscience.visionforge.solid.SolidMaterial @@ -17,7 +17,7 @@ import kotlin.reflect.KClass public object ThreeLineFactory : ThreeFactory { override val type: KClass get() = PolyLine::class - override fun invoke(three: ThreePlugin, obj: PolyLine): Object3D { + override fun build(three: ThreePlugin, obj: PolyLine): Object3D { val geometry = BufferGeometry().apply { setFromPoints(Array((obj.points.size - 1) * 2) { obj.points[ceil(it / 2.0).toInt()].toVector() @@ -25,7 +25,7 @@ public object ThreeLineFactory : ThreeFactory { } val material = ThreeMaterials.getLineMaterial( - obj.computePropertyNode(SolidMaterial.MATERIAL_KEY), + obj.getProperty(SolidMaterial.MATERIAL_KEY), false ) diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeMaterials.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeMaterials.kt index 82b088e5..e0f3a4a6 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeMaterials.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeMaterials.kt @@ -171,7 +171,7 @@ public fun Mesh.updateMaterialProperty(vision: Vision, propertyName: Name) { ?: ThreeMaterials.BLACK_COLOR } SolidMaterial.MATERIAL_OPACITY_KEY -> { - val opacity = vision.getPropertyValue( + val opacity = vision.getProperty( SolidMaterial.MATERIAL_OPACITY_KEY, inherit = true, )?.double ?: 1.0 @@ -179,7 +179,7 @@ public fun Mesh.updateMaterialProperty(vision: Vision, propertyName: Name) { material.transparent = opacity < 1.0 } SolidMaterial.MATERIAL_WIREFRAME_KEY -> { - material.asDynamic().wireframe = vision.getPropertyValue( + material.asDynamic().wireframe = vision.getProperty( SolidMaterial.MATERIAL_WIREFRAME_KEY, inherit = true, )?.boolean ?: false diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreePlugin.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreePlugin.kt index 182ec009..03ed5ee4 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreePlugin.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreePlugin.kt @@ -12,7 +12,6 @@ import space.kscience.visionforge.Vision import space.kscience.visionforge.onPropertyChange import space.kscience.visionforge.solid.* import space.kscience.visionforge.solid.specifications.Canvas3DOptions -import space.kscience.visionforge.solid.three.set import space.kscience.visionforge.visible import kotlin.collections.set import kotlin.reflect.KClass @@ -49,10 +48,10 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer { public fun buildObject3D(obj: Solid): Object3D = when (obj) { is ThreeJsVision -> obj.render(this) - is SolidReferenceGroup -> ThreeReferenceFactory(this, obj) + is SolidReferenceGroup -> ThreeReferenceFactory.build(this, obj) is SolidGroup -> { val group = ThreeGroup() - obj.children.forEach { (token, child) -> + obj.items.forEach { (token, child) -> if (child is Solid && token != SolidGroup.PROTOTYPES_TOKEN && child.ignore != true) { try { val object3D = buildObject3D(child) @@ -101,13 +100,13 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer { } } } - is Composite -> compositeFactory(this, obj) + is Composite -> compositeFactory.build(this, obj) else -> { //find specialized factory for this type if it is present val factory: ThreeFactory? = findObjectFactory(obj::class) when { - factory != null -> factory(this, obj) - obj is GeometrySolid -> ThreeShapeFactory(this, obj) + factory != null -> factory.build(this, obj) + obj is GeometrySolid -> ThreeShapeFactory.build(this, obj) else -> error("Renderer for ${obj::class} not found") } } diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreePointLightFactory.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreePointLightFactory.kt index 727aa098..e6c84c94 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreePointLightFactory.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreePointLightFactory.kt @@ -11,17 +11,19 @@ import kotlin.reflect.KClass public object ThreePointLightFactory : ThreeFactory { override val type: KClass get() = PointLightSource::class - override fun invoke(three: ThreePlugin, obj: PointLightSource): PointLight { + private val DEFAULT_COLOR = Color(0x404040) + + override fun build(three: ThreePlugin, obj: PointLightSource): PointLight { val res = PointLight().apply { matrixAutoUpdate = false - color = obj.color.threeColor() ?: Color(0x404040) + color = obj.color.threeColor() ?: DEFAULT_COLOR intensity = obj.intensity.toDouble() updatePosition(obj) } obj.onPropertyChange { name -> when (name) { - LightSource::color.name.asName() -> res.color = obj.color.threeColor() ?: Color(0x404040) + LightSource::color.name.asName() -> res.color = obj.color.threeColor() ?: DEFAULT_COLOR LightSource::intensity.name.asName() -> res.intensity = obj.intensity.toDouble() else -> res.updateProperty(obj, name) } @@ -29,4 +31,5 @@ public object ThreePointLightFactory : ThreeFactory { return res } + } \ No newline at end of file diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeReferenceFactory.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeReferenceFactory.kt index aa779e46..8e667dcb 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeReferenceFactory.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeReferenceFactory.kt @@ -30,7 +30,7 @@ public object ThreeReferenceFactory : ThreeFactory { } } - override fun invoke(three: ThreePlugin, obj: SolidReferenceGroup): Object3D { + override fun build(three: ThreePlugin, obj: SolidReferenceGroup): Object3D { val template = obj.prototype val cachedObject = cache.getOrPut(template) { three.buildObject3D(template) @@ -50,7 +50,7 @@ public object ThreeReferenceFactory : ThreeFactory { 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[childName] ?: error("Reference child with name '$childName' not found") + val referenceChild = obj.children[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 {