[WIP] great refactoring in progress

This commit is contained in:
Alexander Nozik 2022-08-04 21:36:00 +03:00
parent 4b1149b99b
commit 791d6d7a81
No known key found for this signature in database
GPG Key ID: F7FCF2DD25C71357
97 changed files with 1316 additions and 1181 deletions

View File

@ -3,7 +3,7 @@ plugins {
// id("org.jetbrains.kotlinx.kover") version "0.5.0" // 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") val fxVersion by extra("11")
allprojects{ allprojects{

View File

@ -1,10 +1,14 @@
package ru.mipt.npm.root 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.Name
import space.kscience.dataforge.names.plus import space.kscience.dataforge.names.plus
import space.kscience.dataforge.values.doubleArray import space.kscience.dataforge.values.doubleArray
import space.kscience.visionforge.isEmpty import space.kscience.visionforge.isEmpty
import space.kscience.visionforge.setPropertyValue
import space.kscience.visionforge.solid.* import space.kscience.visionforge.solid.*
import space.kscience.visionforge.solid.SolidMaterial.Companion.MATERIAL_COLOR_KEY import space.kscience.visionforge.solid.SolidMaterial.Companion.MATERIAL_COLOR_KEY
import kotlin.math.* import kotlin.math.*
@ -20,7 +24,7 @@ private fun degToRad(d: Double) = d * PI / 180.0
private data class RootToSolidContext( private data class RootToSolidContext(
val prototypeHolder: PrototypeHolder, val prototypeHolder: PrototypeHolder,
val currentLayer: Int = 0, val currentLayer: Int = 0,
val maxLayer: Int = 5 val maxLayer: Int = 5,
) )
// converting to XYZ to TaitBryan angles according to https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix // converting to XYZ to TaitBryan angles according to https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
@ -42,14 +46,17 @@ private fun Solid.useMatrix(matrix: DGeoMatrix?) {
"TGeoIdentity" -> { "TGeoIdentity" -> {
//do nothing //do nothing
} }
"TGeoTranslation" -> { "TGeoTranslation" -> {
val fTranslation by matrix.meta.doubleArray() val fTranslation by matrix.meta.doubleArray()
translate(fTranslation) translate(fTranslation)
} }
"TGeoRotation" -> { "TGeoRotation" -> {
val fRotationMatrix by matrix.meta.doubleArray() val fRotationMatrix by matrix.meta.doubleArray()
rotate(fRotationMatrix) rotate(fRotationMatrix)
} }
"TGeoCombiTrans" -> { "TGeoCombiTrans" -> {
val fTranslation by matrix.meta.doubleArray() val fTranslation by matrix.meta.doubleArray()
@ -58,6 +65,7 @@ private fun Solid.useMatrix(matrix: DGeoMatrix?) {
rotate(it.doubleArray) rotate(it.doubleArray)
} }
} }
"TGeoHMatrix" -> { "TGeoHMatrix" -> {
val fTranslation by matrix.meta.doubleArray() val fTranslation by matrix.meta.doubleArray()
val fRotationMatrix by matrix.meta.doubleArray() val fRotationMatrix by matrix.meta.doubleArray()
@ -73,7 +81,7 @@ private fun SolidGroup.addShape(
shape: DGeoShape, shape: DGeoShape,
context: RootToSolidContext, context: RootToSolidContext,
name: String? = shape.fName.ifEmpty { null }, name: String? = shape.fName.ifEmpty { null },
block: Solid.() -> Unit = {} block: Solid.() -> Unit = {},
) { ) {
when (shape.typename) { when (shape.typename) {
"TGeoCompositeShape" -> { "TGeoCompositeShape" -> {
@ -94,6 +102,7 @@ private fun SolidGroup.addShape(
} }
}.apply(block) }.apply(block)
} }
"TGeoXtru" -> { "TGeoXtru" -> {
val fNvert by shape.meta.int(0) val fNvert by shape.meta.int(0)
val fX by shape.meta.doubleArray() val fX by shape.meta.doubleArray()
@ -121,6 +130,7 @@ private fun SolidGroup.addShape(
} }
}.apply(block) }.apply(block)
} }
"TGeoTube" -> { "TGeoTube" -> {
val fRmax by shape.meta.double(0.0) val fRmax by shape.meta.double(0.0)
val fDz by shape.meta.double(0.0) val fDz by shape.meta.double(0.0)
@ -134,6 +144,7 @@ private fun SolidGroup.addShape(
block = block block = block
) )
} }
"TGeoTubeSeg" -> { "TGeoTubeSeg" -> {
val fRmax by shape.meta.double(0.0) val fRmax by shape.meta.double(0.0)
val fDz by shape.meta.double(0.0) val fDz by shape.meta.double(0.0)
@ -151,6 +162,7 @@ private fun SolidGroup.addShape(
block = block block = block
) )
} }
"TGeoPcon" -> { "TGeoPcon" -> {
val fDphi by shape.meta.double(0.0) val fDphi by shape.meta.double(0.0)
val fNz by shape.meta.int(2) val fNz by shape.meta.int(2)
@ -176,6 +188,7 @@ private fun SolidGroup.addShape(
TODO() TODO()
} }
} }
"TGeoPgon" -> { "TGeoPgon" -> {
//TODO add a inner polygone layer //TODO add a inner polygone layer
val fDphi by shape.meta.double(0.0) val fDphi by shape.meta.double(0.0)
@ -206,15 +219,18 @@ private fun SolidGroup.addShape(
} }
}.apply(block) }.apply(block)
} }
"TGeoShapeAssembly" -> { "TGeoShapeAssembly" -> {
val fVolume by shape.dObject(::DGeoVolume) val fVolume by shape.dObject(::DGeoVolume)
fVolume?.let { volume -> fVolume?.let { volume ->
addRootVolume(volume, context, block = block) addRootVolume(volume, context, block = block)
} }
} }
"TGeoBBox" -> { "TGeoBBox" -> {
box(shape.fDX * 2, shape.fDY * 2, shape.fDZ * 2, name = name, block = block) box(shape.fDX * 2, shape.fDY * 2, shape.fDZ * 2, name = name, block = block)
} }
"TGeoTrap" -> { "TGeoTrap" -> {
val fTheta by shape.meta.double(0.0) val fTheta by shape.meta.double(0.0)
val fPhi 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) val node8 = Point3D(-fTl2, fH2, fDz)
hexagon(node1, node2, node3, node4, node5, node6, node7, node8, name) hexagon(node1, node2, node3, node4, node5, node6, node7, node8, name)
} }
"TGeoScaledShape" -> { "TGeoScaledShape" -> {
val fShape by shape.dObject(::DGeoShape) val fShape by shape.dObject(::DGeoShape)
val fScale by shape.dObject(::DGeoScale) val fScale by shape.dObject(::DGeoScale)
@ -253,6 +270,7 @@ private fun SolidGroup.addShape(
} }
} }
} }
else -> { else -> {
TODO("A shape with type ${shape.typename} not implemented") 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) val fMatrix by obj.dObject(::DGeoMatrix)
this.useMatrix(fMatrix) this.useMatrix(fMatrix)
} }
"TGeoNodeOffset" -> { "TGeoNodeOffset" -> {
val fOffset by obj.meta.double(0.0) val fOffset by obj.meta.double(0.0)
x = fOffset x = fOffset
@ -301,10 +320,10 @@ private fun buildVolume(volume: DGeoVolume, context: RootToSolidContext): Solid?
} }
} }
} }
return if (group.isEmpty()) { return if (group.children.isEmpty()) {
null null
} else if (group.children.size == 1 && group.meta.isEmpty()) { } else if (group.items.size == 1 && group.meta.isEmpty()) {
(group.children.values.first() as Solid).apply { parent = null } group.items.values.first().apply { parent = null }
} else { } else {
group group
} }
@ -317,7 +336,7 @@ private fun SolidGroup.addRootVolume(
context: RootToSolidContext, context: RootToSolidContext,
name: String? = null, name: String? = null,
cache: Boolean = true, cache: Boolean = true,
block: Solid.() -> Unit = {} block: Solid.() -> Unit = {},
) { ) {
val combinedName = if (volume.fName.isEmpty()) { val combinedName = if (volume.fName.isEmpty()) {
name name
@ -330,7 +349,7 @@ private fun SolidGroup.addRootVolume(
if (!cache) { if (!cache) {
val group = buildVolume(volume, context)?.apply { val group = buildVolume(volume, context)?.apply {
volume.fFillColor?.let { volume.fFillColor?.let {
meta[MATERIAL_COLOR_KEY] = RootColors[it] setPropertyValue(MATERIAL_COLOR_KEY, RootColors[it])
} }
block() block()
} }
@ -347,7 +366,7 @@ private fun SolidGroup.addRootVolume(
ref(templateName, name).apply { ref(templateName, name).apply {
volume.fFillColor?.let { volume.fFillColor?.let {
meta[MATERIAL_COLOR_KEY] = RootColors[it] setPropertyValue(MATERIAL_COLOR_KEY, RootColors[it])
} }
block() block()
} }

View File

@ -160,7 +160,7 @@ private fun SolidGroup.volume(volume: TGeoVolume, name: String? = null, cache: B
name = combinedName, name = combinedName,
obj = group, obj = group,
prototypeHolder = rootPrototypes, prototypeHolder = rootPrototypes,
templateName = volumesName + Name.parse(combinedName ?: "volume[${group.hashCode()}]") prototypeName = volumesName + Name.parse(combinedName ?: "volume[${group.hashCode()}]")
) )
} }

View File

@ -1,13 +1,13 @@
package space.kscience.visionforge.gdml package space.kscience.visionforge.gdml
import space.kscience.dataforge.meta.string
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.values.asValue import space.kscience.dataforge.values.asValue
import space.kscience.dataforge.values.string
import space.kscience.gdml.GdmlShowCase import space.kscience.gdml.GdmlShowCase
import space.kscience.visionforge.Vision import space.kscience.visionforge.Vision
import space.kscience.visionforge.computeProperties
import space.kscience.visionforge.get 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.Solid
import space.kscience.visionforge.solid.SolidMaterial import space.kscience.visionforge.solid.SolidMaterial
import space.kscience.visionforge.solid.material import space.kscience.visionforge.solid.material
@ -20,8 +20,8 @@ class GDMLVisionTest {
@Test @Test
fun testCubesStyles(){ fun testCubesStyles(){
val segment = cubes["composite-000.segment-0"] as Solid val segment = cubes.children["composite-000.segment-0"] as Solid
println(segment.computeProperties().getValue(Vision.STYLE_KEY)) println(segment.getPropertyValue(Vision.STYLE_KEY))
// println(segment.computePropertyNode(SolidMaterial.MATERIAL_KEY)) // println(segment.computePropertyNode(SolidMaterial.MATERIAL_KEY))
// println(segment.computeProperty(SolidMaterial.MATERIAL_COLOR_KEY)) // println(segment.computeProperty(SolidMaterial.MATERIAL_COLOR_KEY))
@ -35,7 +35,7 @@ class GDMLVisionTest {
fun testPrototypeProperty() { fun testPrototypeProperty() {
val child = cubes[Name.of("composite-000","segment-0")] val child = cubes[Name.of("composite-000","segment-0")]
assertNotNull(child) assertNotNull(child)
child.setProperty(SolidMaterial.MATERIAL_COLOR_KEY, "red".asValue()) child.setPropertyValue(SolidMaterial.MATERIAL_COLOR_KEY, "red".asValue())
assertEquals("red", child.getPropertyValue(SolidMaterial.MATERIAL_COLOR_KEY)?.string) assertEquals("red", child.getProperty(SolidMaterial.MATERIAL_COLOR_KEY).string)
} }
} }

View File

@ -26,7 +26,6 @@ import space.kscience.visionforge.setAsRoot
import space.kscience.visionforge.solid.Solid import space.kscience.visionforge.solid.Solid
import space.kscience.visionforge.solid.Solids import space.kscience.visionforge.solid.Solids
import space.kscience.visionforge.solid.ambientLight import space.kscience.visionforge.solid.ambientLight
import space.kscience.visionforge.solid.invoke
import styled.css import styled.css
import styled.styledDiv import styled.styledDiv

View File

@ -10,7 +10,6 @@ import space.kscience.visionforge.Colors
import space.kscience.visionforge.gdml.toVision import space.kscience.visionforge.gdml.toVision
import space.kscience.visionforge.react.render import space.kscience.visionforge.react.render
import space.kscience.visionforge.solid.ambientLight import space.kscience.visionforge.solid.ambientLight
import space.kscience.visionforge.solid.invoke
import space.kscience.visionforge.solid.three.ThreePlugin import space.kscience.visionforge.solid.three.ThreePlugin
import space.kscience.visionforge.startApplication import space.kscience.visionforge.startApplication
import styled.injectGlobal import styled.injectGlobal

View File

@ -75,7 +75,7 @@ private class JsPlaygroundApp : Application {
context = playgroundContext context = playgroundContext
solid { solid {
ambientLight { ambientLight {
color(Colors.white) color.set(Colors.white)
} }
repeat(100) { repeat(100) {
sphere(5, name = "sphere[$it]") { sphere(5, name = "sphere[$it]") {
@ -83,7 +83,7 @@ private class JsPlaygroundApp : Application {
y = random.nextDouble(-300.0, 300.0) y = random.nextDouble(-300.0, 300.0)
z = random.nextDouble(-300.0, 300.0) z = random.nextDouble(-300.0, 300.0)
material { material {
color(random.nextInt()) color.set(random.nextInt())
} }
detail = 16 detail = 16
} }

View File

@ -43,13 +43,13 @@ val GravityDemo = fc<DemoProps> { props ->
context = props.context context = props.context
solid { solid {
pointLight(200, 200, 200, name = "light"){ pointLight(200, 200, 200, name = "light"){
color(Colors.white) color.set(Colors.white)
} }
ambientLight() ambientLight()
sphere(5.0, "ball") { sphere(5.0, "ball") {
detail = 16 detail = 16
color("red") color.set("red")
val h = 100.0 val h = 100.0
y = h y = h
context.launch { context.launch {

View File

@ -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.CENTRAL_LAYER_Z
import ru.mipt.npm.muon.monitor.Monitor.LOWER_LAYER_Z import ru.mipt.npm.muon.monitor.Monitor.LOWER_LAYER_Z
import ru.mipt.npm.muon.monitor.Monitor.UPPER_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.VisionManager
import space.kscience.visionforge.removeAll
import space.kscience.visionforge.setAsRoot import space.kscience.visionforge.setAsRoot
import space.kscience.visionforge.setProperty
import space.kscience.visionforge.solid.* 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.set
import kotlin.collections.toTypedArray
import kotlin.math.PI import kotlin.math.PI
class Model(val manager: VisionManager) { class Model(val manager: VisionManager) {
private val map = HashMap<String, SolidGroup>() private val map = HashMap<String, SolidGroup>()
private val events = HashSet<Event>() private val events = HashSet<Event>()
private fun SolidGroup.pixel(pixel: SC1) { private fun VisionContainerBuilder<Solid>.pixel(pixel: SC1) {
val group = group(pixel.name) { val group = group(pixel.name) {
position = Point3D(pixel.center.x, pixel.center.y, pixel.center.z) position = Point3D(pixel.center.x, pixel.center.y, pixel.center.z)
box(pixel.xSize, pixel.ySize, pixel.zSize) box(pixel.xSize, pixel.ySize, pixel.zSize)
@ -45,7 +39,7 @@ class Model(val manager: VisionManager) {
val root: SolidGroup = SolidGroup().apply { val root: SolidGroup = SolidGroup().apply {
setAsRoot(this@Model.manager) setAsRoot(this@Model.manager)
material { material {
color("darkgreen") color.set("darkgreen")
} }
rotationX = PI / 2 rotationX = PI / 2
group("bottom") { group("bottom") {
@ -70,14 +64,14 @@ class Model(val manager: VisionManager) {
private fun highlight(pixel: String) { private fun highlight(pixel: String) {
println("highlight $pixel") println("highlight $pixel")
map[pixel]?.color?.invoke("blue") map[pixel]?.color.set("blue")
} }
fun reset() { fun reset() {
map.values.forEach { 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) { fun displayEvent(event: Event) {
@ -89,7 +83,7 @@ class Model(val manager: VisionManager) {
event.track?.let { event.track?.let {
tracks.polyline(*it.toTypedArray(), name = "track[${event.id}]") { tracks.polyline(*it.toTypedArray(), name = "track[${event.id}]") {
thickness = 4 thickness = 4
color("red") color.set("red")
} }
} }
} }

View File

@ -25,7 +25,6 @@ import space.kscience.visionforge.react.flexRow
import space.kscience.visionforge.ring.ThreeCanvasWithControls import space.kscience.visionforge.ring.ThreeCanvasWithControls
import space.kscience.visionforge.ring.tab import space.kscience.visionforge.ring.tab
import space.kscience.visionforge.solid.ambientLight import space.kscience.visionforge.solid.ambientLight
import space.kscience.visionforge.solid.invoke
import space.kscience.visionforge.solid.specifications.Canvas3DOptions import space.kscience.visionforge.solid.specifications.Canvas3DOptions
import space.kscience.visionforge.solid.three.edges import space.kscience.visionforge.solid.three.edges
import styled.css import styled.css

View File

@ -7,7 +7,7 @@ import space.kscience.visionforge.gdml.toVision
import space.kscience.visionforge.html.ResourceLocation import space.kscience.visionforge.html.ResourceLocation
import space.kscience.visionforge.solid.Solids import space.kscience.visionforge.solid.Solids
import space.kscience.visionforge.solid.color import space.kscience.visionforge.solid.color
import space.kscience.visionforge.solid.invoke import space.kscience.visionforge.solid.set
import space.kscience.visionforge.visible import space.kscience.visionforge.visible
import java.nio.file.Path import java.nio.file.Path
@ -229,7 +229,7 @@ fun main() = makeVisionFile(Path.of("curves.html"), resourceLocation = ResourceL
visible = false visible = false
} }
if(solid.name.startsWith("gas")){ if(solid.name.startsWith("gas")){
color("green") color.set("green")
} else { } else {
//make all solids semi-transparent //make all solids semi-transparent
transparent() transparent()

View File

@ -5,7 +5,7 @@ import space.kscience.visionforge.Colors
import space.kscience.visionforge.gdml.gdml import space.kscience.visionforge.gdml.gdml
import space.kscience.visionforge.solid.Solids import space.kscience.visionforge.solid.Solids
import space.kscience.visionforge.solid.ambientLight import space.kscience.visionforge.solid.ambientLight
import space.kscience.visionforge.solid.invoke import space.kscience.visionforge.solid.set
import space.kscience.visionforge.solid.solid import space.kscience.visionforge.solid.solid
fun main() = makeVisionFile { fun main() = makeVisionFile {
@ -13,7 +13,7 @@ fun main() = makeVisionFile {
requirePlugin(Solids) requirePlugin(Solids)
solid { solid {
ambientLight { ambientLight {
color(Colors.white) color.set(Colors.white)
} }
gdml(GdmlShowCase.babyIaxo(), "D0") gdml(GdmlShowCase.babyIaxo(), "D0")
} }

View File

@ -19,7 +19,7 @@ fun main() = makeVisionFile(
vision { vision {
solid { solid {
ambientLight { ambientLight {
color(Colors.white) color.set(Colors.white)
} }
repeat(100) { repeat(100) {
sphere(5, name = "sphere[$it]") { sphere(5, name = "sphere[$it]") {
@ -27,7 +27,7 @@ fun main() = makeVisionFile(
y = random.nextDouble(-300.0, 300.0) y = random.nextDouble(-300.0, 300.0)
z = random.nextDouble(-300.0, 300.0) z = random.nextDouble(-300.0, 300.0)
material { material {
color(random.nextInt()) color.set(random.nextInt())
} }
detail = 16 detail = 16
} }

View File

@ -2,8 +2,8 @@ package space.kscience.visionforge.examples
import space.kscience.visionforge.html.ResourceLocation import space.kscience.visionforge.html.ResourceLocation
import space.kscience.visionforge.solid.box import space.kscience.visionforge.solid.box
import space.kscience.visionforge.solid.invoke
import space.kscience.visionforge.solid.material import space.kscience.visionforge.solid.material
import space.kscience.visionforge.solid.set
import space.kscience.visionforge.solid.solid import space.kscience.visionforge.solid.solid
fun main() = makeVisionFile(resourceLocation = ResourceLocation.SYSTEM) { fun main() = makeVisionFile(resourceLocation = ResourceLocation.SYSTEM) {
@ -11,7 +11,7 @@ fun main() = makeVisionFile(resourceLocation = ResourceLocation.SYSTEM) {
solid { solid {
box(100, 100, 100) box(100, 100, 100)
material { material {
emissiveColor("red") emissiveColor.set("red")
} }
} }
} }

View File

@ -15,7 +15,7 @@ internal fun visionOfSatellite(
ySegmentSize: Number = xSegmentSize, ySegmentSize: Number = xSegmentSize,
fiberDiameter: Number = 1.0, fiberDiameter: Number = 1.0,
): SolidGroup = SolidGroup { ): SolidGroup = SolidGroup {
color("darkgreen") color.set("darkgreen")
val transparent by style { val transparent by style {
this[SolidMaterial.MATERIAL_OPACITY_KEY] = 0.3 this[SolidMaterial.MATERIAL_OPACITY_KEY] = 0.3
} }

View File

@ -44,7 +44,7 @@ fun main() {
val randomJ = Random.nextInt(1, 4) val randomJ = Random.nextInt(1, 4)
val target = Name.parse("layer[$randomLayer].segment[$randomI,$randomJ]") val target = Name.parse("layer[$randomLayer].segment[$randomI,$randomJ]")
val targetVision = sat[target] as Solid val targetVision = sat[target] as Solid
targetVision.color("red") targetVision.color.set("red")
delay(1000) delay(1000)
targetVision.color.clear() targetVision.color.clear()
delay(500) delay(500)

View File

@ -20,7 +20,7 @@ fun VisionLayout<Solid>.demo(name: String, title: String = name, block: SolidGro
} }
val vision = SolidGroup(block).apply { val vision = SolidGroup(block).apply {
ambientLight{ ambientLight{
color(Colors.white) color.set(Colors.white)
} }
} }
render(Name.parse(name), vision, meta) render(Name.parse(name), vision, meta)
@ -46,23 +46,23 @@ fun VisionLayout<Solid>.showcase() {
ambientLight() ambientLight()
box(100.0, 100.0, 100.0) { box(100.0, 100.0, 100.0) {
z = -110.0 z = -110.0
color("teal") color.set("teal")
} }
sphere(50.0) { sphere(50.0) {
x = 110 x = 110
detail = 16 detail = 16
color("red") color.set("red")
} }
tube(50, height = 10, innerRadius = 25, angle = PI) { tube(50, height = 10, innerRadius = 25, angle = PI) {
y = 110 y = 110
detail = 16 detail = 16
rotationX = PI / 4 rotationX = PI / 4
color("blue") color.set("blue")
} }
sphereLayer(50, 40, theta = PI / 2) { sphereLayer(50, 40, theta = PI / 2) {
rotationX = -PI * 3 / 4 rotationX = -PI * 3 / 4
z = 110 z = 110
color(Colors.pink) color.set(Colors.pink)
} }
} }
@ -77,7 +77,7 @@ fun VisionLayout<Solid>.showcase() {
visible = false visible = false
x = 110.0 x = 110.0
//override color for this cube //override color for this cube
color(1530) color.set(1530)
GlobalScope.launch(Dispatchers.Main) { GlobalScope.launch(Dispatchers.Main) {
while (isActive) { while (isActive) {
@ -92,7 +92,7 @@ fun VisionLayout<Solid>.showcase() {
val random = Random(111) val random = Random(111)
while (isActive) { while (isActive) {
delay(1000) 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<Solid>.showcase() {
rotationY = PI / 4 rotationY = PI / 4
box(100, 100, 100) { box(100, 100, 100) {
rotationZ = PI / 4 rotationZ = PI / 4
color(Colors.red) color.set(Colors.red)
} }
} }
} }
@ -117,7 +117,7 @@ fun VisionLayout<Solid>.showcase() {
for (i in 0..100) { for (i in 0..100) {
layer(i * 5, 20 * sin(2 * PI / 100 * i), 20 * cos(2 * PI / 100 * i)) 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 rotationX = -PI / 2
} }
} }
@ -126,13 +126,13 @@ fun VisionLayout<Solid>.showcase() {
sphere(100) { sphere(100) {
detail = 32 detail = 32
opacity = 0.4 opacity = 0.4
color(Colors.blue) color.set(Colors.blue)
} }
repeat(20) { repeat(20) {
polyline(Point3D(100, 100, 100), Point3D(-100, -100, -100)) { polyline(Point3D(100, 100, 100), Point3D(-100, -100, -100)) {
thickness = 3.0 thickness = 3.0
rotationX = it * PI2 / 20 rotationX = it * PI2 / 20
color(Colors.green) color.set(Colors.green)
//rotationY = it * PI2 / 20 //rotationY = it * PI2 / 20
} }
} }
@ -159,7 +159,7 @@ fun VisionLayout<Solid>.showcaseCSG() {
detail = 32 detail = 32
} }
material { material {
color(Colors.pink) color.set(Colors.pink)
} }
} }
composite(CompositeType.UNION) { composite(CompositeType.UNION) {
@ -169,7 +169,7 @@ fun VisionLayout<Solid>.showcaseCSG() {
sphere(50) { sphere(50) {
detail = 32 detail = 32
} }
color("lightgreen") color.set("lightgreen")
opacity = 0.7 opacity = 0.7
} }
composite(CompositeType.SUBTRACT) { composite(CompositeType.SUBTRACT) {
@ -180,7 +180,7 @@ fun VisionLayout<Solid>.showcaseCSG() {
sphere(50) { sphere(50) {
detail = 32 detail = 32
} }
color("teal") color.set("teal")
opacity = 0.7 opacity = 0.7
} }
} }
@ -191,7 +191,7 @@ fun VisionLayout<Solid>.showcaseCSG() {
detail = 32 detail = 32
} }
box(100, 100, 100) box(100, 100, 100)
color("red") color.set("red")
opacity = 0.5 opacity = 0.5
} }
} }

View File

@ -11,7 +11,6 @@ import space.kscience.dataforge.names.startsWith
import space.kscience.dataforge.values.asValue import space.kscience.dataforge.values.asValue
import space.kscience.visionforge.onPropertyChange import space.kscience.visionforge.onPropertyChange
import space.kscience.visionforge.set import space.kscience.visionforge.set
import space.kscience.visionforge.setProperty
import space.kscience.visionforge.solid.SolidGroup import space.kscience.visionforge.solid.SolidGroup
import space.kscience.visionforge.solid.layer import space.kscience.visionforge.solid.layer
import space.kscience.visionforge.solid.three.* import space.kscience.visionforge.solid.three.*
@ -72,7 +71,7 @@ internal class VariableBox(val xSize: Number, val ySize: Number) : ThreeJsVision
var value: Int var value: Int
get() = meta[VALUE].int ?: 0 get() = meta[VALUE].int ?: 0
set(value) { set(value) {
setProperty(VALUE, value.asValue()) setPropertyValue(VALUE, value.asValue())
} }
companion object { companion object {

View File

@ -3,7 +3,7 @@
![](../docs/images/hierarchy.png) ![](../docs/images/hierarchy.png)
### Vision ### 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 * 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. Sets the `item` property to the element with the `name` identification.

View File

@ -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 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. `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: The order is this:
* own styles * own styles
* prototypes * prototypes

View File

@ -3,7 +3,7 @@
interface Vision{ interface Vision{
val parent: VisionGroup? val parent: VisionGroup?
fun getPropertyValue(name,inherit,includeStyles,includeDefaults): Value? fun getProperty(name,inherit,includeStyles,includeDefaults): Value?
} }
interface Solid{ interface Solid{
@ -81,7 +81,7 @@ Solid <--- Composite
interface SolidReference{ interface SolidReference{
val prototype: Solid val prototype: Solid
fun getPropertyValue(name,inherit,includeStyles,includeDefaults): Value? fun getProperty(name,inherit,includeStyles,includeDefaults): Value?
} }
VisionGroup <---- SolidReference VisionGroup <---- SolidReference
SolidReferenceGroup -- SolidReference SolidReferenceGroup -- SolidReference
@ -91,7 +91,7 @@ class SolidReferenceGroup{
var properties: MutableMeta? var properties: MutableMeta?
val prototype: Solid val prototype: Solid
val children: Map<NameToken, Vision> val children: Map<NameToken, Vision>
fun getPropertyValue(name,inherit,includeStyles,includeDefaults): Value? fun getProperty(name,inherit,includeStyles,includeDefaults): Value?
} }
VisionBase <-- SolidReferenceGroup VisionBase <-- SolidReferenceGroup
VisionGroup <-- SolidReferenceGroup VisionGroup <-- SolidReferenceGroup

View File

@ -6,4 +6,4 @@ kotlin.jupyter.add.scanner=false
org.gradle.parallel=true org.gradle.parallel=true
org.gradle.jvmargs=-Xmx4G org.gradle.jvmargs=-Xmx4G
toolsVersion=0.11.7-kotlin-1.7.0 toolsVersion=0.11.8-kotlin-1.7.10

View File

@ -10,8 +10,8 @@ import react.fc
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.isEmpty import space.kscience.dataforge.names.isEmpty
import space.kscience.visionforge.Vision import space.kscience.visionforge.Vision
import space.kscience.visionforge.VisionGroup
import space.kscience.visionforge.react.visionTree import space.kscience.visionforge.react.visionTree
import space.kscience.visionforge.solid.SolidGroup
import space.kscience.visionforge.solid.specifications.Canvas3DOptions import space.kscience.visionforge.solid.specifications.Canvas3DOptions
import styled.css import styled.css
import styled.styledDiv import styled.styledDiv
@ -51,7 +51,7 @@ public val ThreeControls: FC<ThreeControlsProps> = fc { props ->
val selectedObject: Vision? = when { val selectedObject: Vision? = when {
selected == null -> null selected == null -> null
selected.isEmpty() -> props.vision selected.isEmpty() -> props.vision
else -> (props.vision as? VisionGroup)?.get(selected) else -> (props.vision as? SolidGroup)?.get(selected)
} }
if (selectedObject != null) { if (selectedObject != null) {
visionPropertyEditor(selectedObject, key = selected) visionPropertyEditor(selectedObject, key = selected)

View File

@ -2,13 +2,14 @@ package space.kscience.visionforge.bootstrap
import org.w3c.dom.Element import org.w3c.dom.Element
import react.RBuilder import react.RBuilder
import react.dom.render import react.dom.client.createRoot
import space.kscience.dataforge.meta.descriptors.MetaDescriptor import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.visionforge.Vision import space.kscience.visionforge.Vision
import space.kscience.visionforge.computeProperties import space.kscience.visionforge.computeProperties
import space.kscience.visionforge.getStyle import space.kscience.visionforge.getStyle
import space.kscience.visionforge.react.metaViewer import space.kscience.visionforge.react.metaViewer
import space.kscience.visionforge.react.propertyEditor import space.kscience.visionforge.react.propertyEditor
import space.kscience.visionforge.react.render
import space.kscience.visionforge.solid.SolidReference import space.kscience.visionforge.solid.SolidReference
import space.kscience.visionforge.styles import space.kscience.visionforge.styles
@ -50,6 +51,6 @@ public fun RBuilder.visionPropertyEditor(
public fun Element.visionPropertyEditor( public fun Element.visionPropertyEditor(
item: Vision, item: Vision,
descriptor: MetaDescriptor? = item.descriptor, descriptor: MetaDescriptor? = item.descriptor,
): Unit = render(this) { ): Unit = createRoot(this).render {
visionPropertyEditor(item, descriptor = descriptor) visionPropertyEditor(item, descriptor = descriptor)
} }

View File

@ -59,9 +59,9 @@ private fun RBuilder.visionTree(props: ObjectTreeProps): Unit {
val obj = props.obj val obj = props.obj
//display as node if any child is visible //display as node if any child is visible
if (obj is VisionGroup) { if (obj is VisionGroup<*>) {
flexRow { flexRow {
if (obj.children.any { !it.key.body.startsWith("@") }) { if (obj.items.any { !it.key.body.startsWith("@") }) {
styledSpan { styledSpan {
css { css {
+TreeStyles.treeCaret +TreeStyles.treeCaret
@ -81,9 +81,9 @@ private fun RBuilder.visionTree(props: ObjectTreeProps): Unit {
css { css {
+TreeStyles.tree +TreeStyles.tree
} }
obj.children.entries obj.items.entries
.filter { !it.key.toString().startsWith("@") } // ignore statics and other hidden children .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) -> .forEach { (childToken, child) ->
styledDiv { styledDiv {
css { css {
@ -92,7 +92,7 @@ private fun RBuilder.visionTree(props: ObjectTreeProps): Unit {
child(ObjectTree) { child(ObjectTree) {
attrs { attrs {
this.name = props.name + childToken this.name = props.name + childToken
this.obj = child this.obj = child.vision
this.selected = props.selected this.selected = props.selected
this.clickCallback = props.clickCallback this.clickCallback = props.clickCallback
} }

View File

@ -108,7 +108,7 @@ public val ThreeCanvasWithControls: FC<ThreeCanvasWithControlsProps> = fc("Three
selected?.let { selected?.let {
when { when {
it.isEmpty() -> solid it.isEmpty() -> solid
else -> (solid as? VisionGroup)?.get(it) else -> (solid as? SolidGroup)?.get(it)
} }
} }
} }
@ -165,7 +165,7 @@ public val ThreeCanvasWithControls: FC<ThreeCanvasWithControlsProps> = fc("Three
} }
IslandContent { IslandContent {
propertyEditor( propertyEditor(
ownProperties = vision.meta, ownProperties = vision.properties(),
allProperties = vision.computeProperties(), allProperties = vision.computeProperties(),
descriptor = vision.descriptor, descriptor = vision.descriptor,
key = selected key = selected

View File

@ -183,7 +183,7 @@ public class space/kscience/visionforge/SimpleVisionPropertyContainer : space/ks
public fun <init> (Lspace/kscience/dataforge/meta/ObservableMutableMeta;)V public fun <init> (Lspace/kscience/dataforge/meta/ObservableMutableMeta;)V
public synthetic fun getMeta ()Lspace/kscience/dataforge/meta/MutableMeta; public synthetic fun getMeta ()Lspace/kscience/dataforge/meta/MutableMeta;
public fun getMeta ()Lspace/kscience/dataforge/meta/ObservableMutableMeta; 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 { 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 fun getManager ()Lspace/kscience/visionforge/VisionManager;
public abstract fun getMeta ()Lspace/kscience/dataforge/meta/ObservableMutableMeta; public abstract fun getMeta ()Lspace/kscience/dataforge/meta/ObservableMutableMeta;
public abstract fun getParent ()Lspace/kscience/visionforge/VisionGroup; public abstract fun getParent ()Lspace/kscience/visionforge/VisionGroup;
public abstract fun getPropertyValue (Lspace/kscience/dataforge/names/Name;ZZZ)Lspace/kscience/dataforge/values/Value; public abstract fun getProperty (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 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 invalidateProperty (Lspace/kscience/dataforge/names/Name;)V
public abstract fun setParent (Lspace/kscience/visionforge/VisionGroup;)V public abstract fun setParent (Lspace/kscience/visionforge/VisionGroup;)V
public abstract fun update (Lspace/kscience/visionforge/VisionChange;)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; protected final fun getOrCreateProperties ()Lspace/kscience/dataforge/meta/MutableMeta;
public fun getParent ()Lspace/kscience/visionforge/VisionGroup; public fun getParent ()Lspace/kscience/visionforge/VisionGroup;
protected final fun getProperties ()Lspace/kscience/dataforge/meta/MutableMeta; 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 invalidateProperty (Lspace/kscience/dataforge/names/Name;)V
public fun setParent (Lspace/kscience/visionforge/VisionGroup;)V public fun setParent (Lspace/kscience/visionforge/VisionGroup;)V
protected final fun setProperties (Lspace/kscience/dataforge/meta/MutableMeta;)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 final class space/kscience/visionforge/VisionKt {
public static final fun getPropertyChanges (Lspace/kscience/visionforge/Vision;)Lkotlinx/coroutines/flow/Flow; 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 final fun getProperty (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 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 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 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 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 interface class space/kscience/visionforge/VisionPropertyContainer {
public abstract fun getMeta ()Lspace/kscience/dataforge/meta/MutableMeta; 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 abstract fun getProperty (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 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 { public final class space/kscience/visionforge/html/HeadersKt {

View File

@ -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<Name>()
override val propertyChanges: SharedFlow<Name> 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)
}
}
}

View File

@ -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<NameToken, Meta>
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)
}
}

View File

@ -9,7 +9,7 @@ import kotlin.properties.ReadOnlyProperty
/** /**
* A reference to a style defined in a specific container * 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 { private tailrec fun styleIsDefined(vision: Vision, reference: StyleReference): Boolean = when {
reference.owner === vision -> true reference.owner === vision -> true
@ -25,7 +25,7 @@ public fun Vision.useStyle(reference: StyleReference) {
} }
@VisionBuilder @VisionBuilder
public fun VisionGroup.style( public fun Vision.style(
styleKey: String? = null, styleKey: String? = null,
builder: MutableMeta.() -> Unit, builder: MutableMeta.() -> Unit,
): ReadOnlyProperty<Any?, StyleReference> = ReadOnlyProperty { _, property -> ): ReadOnlyProperty<Any?, StyleReference> = ReadOnlyProperty { _, property ->
@ -35,7 +35,7 @@ public fun VisionGroup.style(
} }
@VisionBuilder @VisionBuilder
public fun <T : Scheme> VisionGroup.style( public fun <T : Scheme> Vision.style(
spec: Specification<T>, spec: Specification<T>,
styleKey: String? = null, styleKey: String? = null,
builder: T.() -> Unit, builder: T.() -> Unit,

View File

@ -5,7 +5,6 @@ import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.NameToken import space.kscience.dataforge.names.NameToken
import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.asName
import space.kscience.dataforge.names.plus import space.kscience.dataforge.names.plus
import space.kscience.dataforge.values.Value
import space.kscience.dataforge.values.asValue import space.kscience.dataforge.values.asValue
import space.kscience.dataforge.values.stringList import space.kscience.dataforge.values.stringList
import kotlin.jvm.JvmInline import kotlin.jvm.JvmInline
@ -14,11 +13,11 @@ import kotlin.jvm.JvmInline
* A container for styles * A container for styles
*/ */
@JvmInline @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<NameToken, Meta>? get() = styleNode?.items public val items: Map<NameToken, Meta> get() = styleNode.items
public operator fun get(key: String): Meta? = owner.getStyle(key) 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 * Define a style without notifying owner
*/ */
public fun define(key: String, style: Meta?) { 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 * 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) val newStyle = get(key)?.toMutableMeta()?.apply(builder) ?: Meta(builder)
set(key, newStyle.seal()) set(key, newStyle.seal())
} }
@ -61,10 +60,8 @@ internal fun Vision.styleChanged(key: String, oldStyle: Meta?, newStyle: Meta?)
.map { it.asName() } .map { it.asName() }
tokens.forEach { parent?.invalidateProperty(it) } tokens.forEach { parent?.invalidateProperty(it) }
} }
if (this is VisionGroup) { children.values.forEach { vision ->
for (obj in this) { vision.styleChanged(key, oldStyle, newStyle)
obj.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. * List of names of styles applied to this object. Order matters. Not inherited.
*/ */
public var Vision.styles: List<String> public var Vision.styles: List<String>
get() = meta.getValue(Vision.STYLE_KEY)?.stringList ?: emptyList() get() = getPropertyValue(
Vision.STYLE_KEY,
inherit = true,
includeStyles = false,
includeDefaults = false
)?.stringList ?: emptyList()
set(value) { 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, * A stylesheet for this group and its descendants. Stylesheet is not applied directly,
* but instead is just a repository for named configurations. * 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. * 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) { 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) meta.getMeta(StyleSheet.STYLESHEET_KEY + name) ?: parent?.getStyle(name)
/** /**
* Resolve a property from all styles * 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 * Resolve an item in all style layers

View File

@ -1,17 +1,20 @@
package space.kscience.visionforge package space.kscience.visionforge
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch import space.kscience.dataforge.context.Global
import space.kscience.dataforge.meta.* 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.Described
import space.kscience.dataforge.meta.descriptors.MetaDescriptor 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.misc.Type
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.asName
import space.kscience.dataforge.names.parseAsName
import space.kscience.dataforge.names.startsWith import space.kscience.dataforge.names.startsWith
import space.kscience.dataforge.values.Value import space.kscience.dataforge.values.Value
import space.kscience.dataforge.values.asValue import space.kscience.dataforge.values.asValue
@ -23,38 +26,59 @@ import kotlin.reflect.KProperty1
* A root type for display hierarchy * A root type for display hierarchy
*/ */
@Type(TYPE) @Type(TYPE)
public interface Vision : Described, Configurable { public interface Vision : Described {
/** /**
* The parent object of this one. If null, this one is a root. * 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 * 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( public fun getPropertyValue(
name: Name, name: Name,
inherit: Boolean = false, inherit: Boolean,
includeStyles: Boolean = true, includeStyles: Boolean,
includeDefaults: Boolean = true, includeDefaults: Boolean,
): Value? ): 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<Name>
/** /**
* 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) public fun invalidateProperty(propertyName: Name)
@ -68,80 +92,135 @@ public interface Vision : Described, Configurable {
public companion object { public companion object {
public const val TYPE: String = "vision" public const val TYPE: String = "vision"
public val STYLE_KEY: Name = "@style".asName() public val STYLE_KEY: Name = "@style".asName()
public const val STYLE_TARGET: String = "style"
public val VISIBLE_KEY: Name = "visible".asName() public val VISIBLE_KEY: Name = "visible".asName()
} }
} }
/** public fun Vision.getPropertyValue(
* Flow of property invalidation events. It does not contain property values after invalidation since it is not clear name: Name,
* if it should include inherited properties etc. inherit: Boolean? = null,
*/ includeStyles: Boolean? = null,
@OptIn(ExperimentalCoroutinesApi::class) includeDefaults: Boolean = true,
@DFExperimental metaDescriptor: MetaDescriptor? = descriptor?.get(name),
public val Vision.propertyChanges: Flow<Name> ): Value? {
get() = callbackFlow { val inheritFlag = inherit ?: metaDescriptor?.inherited ?: false
meta.onChange(this) { name -> val stylesFlag = includeStyles ?: metaDescriptor?.usesStyles ?: true
launch { return getPropertyValue(name, inheritFlag, stylesFlag, includeDefaults)
send(name) }
}
} public fun Vision.getPropertyValue(
awaitClose { name: String,
meta.removeListener(this) 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) { public fun Vision.getProperty(
meta.onChange(null, callback) 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 * Get [Vision] property using key as a String
*/ */
public fun Vision.getPropertyValue( public fun Vision.getProperty(
key: String, name: String,
inherit: Boolean = false, inherit: Boolean? = null,
includeStyles: Boolean = true, includeStyles: Boolean? = null,
includeDefaults: Boolean = true, 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?) { public fun Vision.properties(
when (item) { inherit: Boolean? = null,
null -> meta.remove(name) useStyles: Boolean? = null,
is Meta -> meta.setMeta(name, item) ): MutableMetaProvider = VisionProperties(this, Name.EMPTY, inherit = inherit, useStyles = useStyles)
is Value -> meta.setValue(name, item)
else -> meta.setValue(name, Value.of(item)) 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?) { public fun Vision.setPropertyValue(name: String, value: Number?): Unit =
setProperty(Name.parse(key), item) 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 * Control visibility of the element
*/ */
public var Vision.visible: Boolean? public var Vision.visible: Boolean?
get() = getPropertyValue(Vision.VISIBLE_KEY)?.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 : Vision, T> V.useProperty( public fun <V : Vision, T> V.useProperty(
property: KProperty1<V, T>, property: KProperty1<V, T>,
owner: Any? = null,
callBack: V.(T) -> Unit, callBack: V.(T) -> Unit,
) { ): Job {
//Pass initial value. //Pass initial value.
callBack(property.get(this)) callBack(property.get(this))
meta.onChange(owner) { name -> return propertyChanges.onEach { name ->
if (name.startsWith(property.name.asName())) { if (name.startsWith(property.name.asName())) {
callBack(property.get(this@useProperty)) callBack(property.get(this@useProperty))
} }
} }.launchIn(manager.context)
}
public interface MutableVisionGroup : Vision {
override val children: MutableVisionChildren
public fun createGroup(): MutableVisionGroup
} }

View File

@ -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<MetaListener> = mutableListOf()
private inner class VisionProperties(val pathName: Name) : ObservableMutableMeta {
override val items: Map<NameToken, ObservableMutableMeta>
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)
//}

View File

@ -7,7 +7,6 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.*
import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.plus import space.kscience.dataforge.names.plus
import space.kscience.dataforge.values.Null import space.kscience.dataforge.values.Null
@ -19,14 +18,13 @@ import kotlin.time.Duration
*/ */
private fun Vision.deepCopy(): Vision { private fun Vision.deepCopy(): Vision {
//Assuming that unrooted visions are already isolated //Assuming that unrooted visions are already isolated
val manager = this.manager ?: return this
//TODO replace by efficient deep copy //TODO replace by efficient deep copy
val json = manager.encodeToJsonElement(this) val json = manager.encodeToJsonElement(this)
return manager.decodeFromJson(json) return manager.decodeFromJson(json)
} }
/** /**
* An update for a [Vision] or a [VisionGroup] * An update for a [Vision]
*/ */
public class VisionChangeBuilder : VisionContainerBuilder<Vision> { public class VisionChangeBuilder : VisionContainerBuilder<Vision> {
@ -87,7 +85,6 @@ public inline fun VisionChange(block: VisionChangeBuilder.() -> Unit): VisionCha
VisionChangeBuilder().apply(block).deepCopy() VisionChangeBuilder().apply(block).deepCopy()
@OptIn(DFExperimental::class)
private fun CoroutineScope.collectChange( private fun CoroutineScope.collectChange(
name: Name, name: Name,
source: Vision, source: Vision,
@ -96,28 +93,25 @@ private fun CoroutineScope.collectChange(
//Collect properties change //Collect properties change
source.onPropertyChange { propertyName -> source.onPropertyChange { propertyName ->
val newItem = source.meta[propertyName] val newItem = source.getProperty(propertyName, false, false, false)
collector().propertyChanged(name, propertyName, newItem) collector().propertyChanged(name, propertyName, newItem)
} }
if (source is VisionGroup) { val children = source.children
//Subscribe for children changes //Subscribe for children changes
source.children.forEach { (token, child) -> for ((token, child) in children) {
collectChange(name + token, child, collector) 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)
}
} }
//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)
} }
/** /**

View File

@ -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<out V : Vision> {
public operator fun get(name: Name): V?
}
public interface VisionContainerBuilder<in V : Vision> {
//TODO add documentation
public operator fun set(name: Name?, child: V?)
}
/**
* A serializable representation of [Vision] children container
*/
public interface VisionChildren : VisionContainer<Vision> {
public val parent: Vision?
public val keys: Set<NameToken>
public val values: Iterable<Vision> get() = keys.map { get(it)!! }
public val changes: Flow<Name>
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<NameToken> get() = emptySet()
override val changes: Flow<Name> get() = emptyFlow()
override fun get(token: NameToken): Vision? = null
}
}
}
public fun VisionChildren.isEmpty(): Boolean = keys.isEmpty()
@Serializable(VisionChildrenContainerSerializer::class)
public interface MutableVisionChildren : VisionChildren, VisionContainerBuilder<Vision> {
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<Pair<NameToken, Vision>> = sequence {
keys.forEach { yield(it to get(it)!!) }
}
public operator fun VisionChildren.iterator(): Iterator<Pair<NameToken, Vision>> = asSequence().iterator()
public operator fun <V : Vision> VisionContainer<V>.get(str: String): V? = get(Name.parse(str))
public operator fun <V : Vision> VisionContainerBuilder<V>.set(
str: String?, vision: V?,
): Unit = set(str?.parseAsName(), vision)
internal class VisionChildrenImpl(
items: Map<NameToken, Vision>,
) : MutableVisionChildren {
override var parent: MutableVisionGroup? = null
internal set
private val items = LinkedHashMap(items)
private val updateJobs = HashMap<NameToken, Job>()
private val scope: CoroutineScope? get() = parent?.manager?.context
override val keys: Set<NameToken> get() = items.keys
override fun get(token: NameToken): Vision? = items[token]
private val _changes = MutableSharedFlow<Name>()
override val changes: SharedFlow<Name> 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<MutableVisionChildren> {
private val mapSerializer = serializer<Map<NameToken,Vision>>()
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)
}
}

View File

@ -1,109 +1,99 @@
package space.kscience.visionforge package space.kscience.visionforge
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.launch import kotlinx.serialization.SerialName
import space.kscience.dataforge.misc.DFExperimental import kotlinx.serialization.Serializable
import space.kscience.dataforge.names.* import kotlinx.serialization.Transient
import space.kscience.dataforge.provider.Provider import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
@DslMarker import space.kscience.dataforge.meta.descriptors.value
public annotation class VisionBuilder import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.NameToken
public interface VisionContainer<out V : Vision> { import space.kscience.dataforge.names.plus
public operator fun get(name: Name): V? 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<Vision> { @Serializable
/** @SerialName("vision.group")
* A map of top level named children public open class VisionGroup : AbstractVision(), MutableVisionGroup {
*/
public val children: Map<NameToken, Vision>
override val defaultTarget: String get() = Vision.TYPE override fun update(change: VisionChange) {
change.children?.forEach { (name, change) ->
/** when {
* A map of direct children for specific target change.delete -> children.set(name, null)
* (currently "visual" or "style") change.vision != null -> children.set(name, change.vision)
*/ else -> children[name]?.update(change)
override fun content(target: String): Map<Name, Any> =
when (target) {
Vision.TYPE -> children.flatMap { (key, value) ->
val res: Map<Name, Any> = 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<Vision> = children.values.iterator()
public fun VisionGroup.isEmpty(): Boolean = this.children.isEmpty()
public interface VisionContainerBuilder<in V : Vision> {
//TODO add documentation
public operator fun set(name: Name?, child: V?)
}
/**
* Mutable version of [VisionGroup]
*/
public interface MutableVisionGroup : VisionGroup, VisionContainerBuilder<Vision> {
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<Name>
get() = callbackFlow {
meta.onChange(this) { name ->
launch {
send(name)
} }
} }
awaitClose { change.properties?.let {
removeStructureListener(this) updateProperties(it, Name.EMPTY)
} }
} }
@SerialName("children")
protected var _children: MutableVisionChildren? = null
public operator fun <V : Vision> VisionContainer<V>.get(str: String): V? = get(Name.parse(str)) @Transient
override val children: MutableVisionChildren = object : MutableVisionChildren {
public operator fun <V : Vision> VisionContainerBuilder<V>.set(token: NameToken, child: V?): Unit = @Synchronized
set(token.asName(), child) fun getOrCreateChildren(): MutableVisionChildren {
if (_children == null) {
_children = VisionChildrenImpl(emptyMap()).apply {
parent = this@VisionGroup
}
}
return _children!!
}
public operator fun <V : Vision> VisionContainerBuilder<V>.set(key: String?, child: V?): Unit = override val parent: MutableVisionGroup get() = this@VisionGroup
set(key?.let(Name::parse), child)
public fun MutableVisionGroup.removeAll(): Unit = children.keys.map { it.asName() }.forEach { this[it] = null } override val keys: Set<NameToken> get() = _children?.keys ?: emptySet()
override val changes: Flow<Name> 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)
//}

View File

@ -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<NameToken, Vision> = LinkedHashMap(),
) : VisionBase(), MutableVisionGroup {
/**
* A map of top level named children
*/
override val children: Map<NameToken, Vision> 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<StructureChangeListener>()
@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)
}

View File

@ -25,13 +25,14 @@ public class VisionManager(meta: Meta) : AbstractPlugin(meta) {
/** /**
* Combined [SerializersModule] for all registered visions * Combined [SerializersModule] for all registered visions
*/ */
public val serializersModule: SerializersModule public val serializersModule: SerializersModule by lazy {
get() = SerializersModule { SerializersModule {
include(defaultSerialModule) include(defaultSerialModule)
context.gather<SerializersModule>(VISION_SERIALIZER_MODULE_TARGET).values.forEach { context.gather<SerializersModule>(VISION_SERIALIZER_MODULE_TARGET).values.forEach {
include(it) include(it)
} }
} }
}
public val jsonFormat: Json public val jsonFormat: Json
get() = Json(defaultJson) { get() = Json(defaultJson) {
@ -67,9 +68,8 @@ public class VisionManager(meta: Meta) : AbstractPlugin(meta) {
private val defaultSerialModule: SerializersModule = SerializersModule { private val defaultSerialModule: SerializersModule = SerializersModule {
polymorphic(Vision::class) { polymorphic(Vision::class) {
default { VisionBase.serializer() } default { VisionGroup.serializer() }
subclass(VisionBase.serializer()) subclass(VisionGroup.serializer())
subclass(VisionGroupBase.serializer())
subclass(VisionOfNumberField.serializer()) subclass(VisionOfNumberField.serializer())
subclass(VisionOfTextField.serializer()) subclass(VisionOfTextField.serializer())
subclass(VisionOfCheckbox.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 val Context.visionManager: VisionManager get() = fetch(VisionManager)
public fun Vision.encodeToString(): String = public fun Vision.encodeToString(): String = manager.encodeToString(this)
manager?.encodeToString(this) ?: error("VisionManager not defined in Vision")
/**
* 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)
}

View File

@ -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<NameToken, MutableMeta>
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)
// }
//}

View File

@ -1,34 +1,29 @@
package space.kscience.visionforge 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.MutableMeta
import space.kscience.dataforge.meta.ObservableMutableMeta
import space.kscience.dataforge.meta.get
import space.kscience.dataforge.names.Name 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 * Property containers are used to create a symmetric behaviors for vision properties and style builders
*/ */
public interface VisionPropertyContainer<out V : Vision> { public interface VisionPropertyContainer<out V : Vision> {
public val meta: MutableMeta public fun getProperty(
public fun getPropertyValue(
name: Name,
inherit: Boolean = false,
includeStyles: Boolean = true,
includeDefaults: Boolean = true,
): Value?
}
public open class SimpleVisionPropertyContainer<out V : Vision>(
override val meta: ObservableMutableMeta,
) : VisionPropertyContainer<V>, Configurable {
override fun getPropertyValue(
name: Name, name: Name,
inherit: Boolean, inherit: Boolean,
includeStyles: Boolean, includeStyles: Boolean,
includeDefaults: Boolean includeDefaults: Boolean,
): Value? = meta[name]?.value ): Meta?
}
public open class SimpleVisionPropertyContainer<out V : Vision>(
public val meta: MutableMeta,
) : VisionPropertyContainer<V> {
override fun getProperty(
name: Name,
inherit: Boolean,
includeStyles: Boolean,
includeDefaults: Boolean,
): Meta? = meta.getMeta(name)
} }

View File

@ -9,13 +9,14 @@ import kotlinx.serialization.Serializable
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.get import space.kscience.dataforge.meta.get
import space.kscience.dataforge.meta.node import space.kscience.dataforge.meta.node
import space.kscience.visionforge.properties
@Serializable @Serializable
@SerialName("html.form") @SerialName("html.form")
public class VisionOfHtmlForm( public class VisionOfHtmlForm(
public val formId: String, public val formId: String,
) : VisionOfHtmlInput() { ) : VisionOfHtmlInput() {
public var values: Meta? by meta.node() public var values: Meta? by properties().node()
} }
public class HtmlFormFragment internal constructor( public class HtmlFormFragment internal constructor(

View File

@ -5,11 +5,12 @@ import kotlinx.serialization.Serializable
import space.kscience.dataforge.meta.boolean import space.kscience.dataforge.meta.boolean
import space.kscience.dataforge.meta.number import space.kscience.dataforge.meta.number
import space.kscience.dataforge.meta.string import space.kscience.dataforge.meta.string
import space.kscience.visionforge.VisionBase import space.kscience.visionforge.VisionGroup
import space.kscience.visionforge.properties
@Serializable @Serializable
public abstract class VisionOfHtmlInput : VisionBase() { public abstract class VisionOfHtmlInput : VisionGroup() {
public var disabled: Boolean by meta.boolean(false) public var disabled: Boolean by properties().boolean(false)
} }
@Serializable @Serializable
@ -18,7 +19,7 @@ public class VisionOfTextField(
public val label: String? = null, public val label: String? = null,
public val name: String? = null, public val name: String? = null,
) : VisionOfHtmlInput() { ) : VisionOfHtmlInput() {
public var text: String? by meta.string() public var text: String? by properties().string()
} }
@Serializable @Serializable
@ -27,7 +28,7 @@ public class VisionOfCheckbox(
public val label: String? = null, public val label: String? = null,
public val name: String? = null, public val name: String? = null,
) : VisionOfHtmlInput() { ) : VisionOfHtmlInput() {
public var checked: Boolean? by meta.boolean() public var checked: Boolean? by properties().boolean()
} }
@Serializable @Serializable
@ -36,7 +37,7 @@ public class VisionOfNumberField(
public val label: String? = null, public val label: String? = null,
public val name: String? = null, public val name: String? = null,
) : VisionOfHtmlInput() { ) : VisionOfHtmlInput() {
public var value: Number? by meta.number() public var value: Number? by properties().number()
} }
@Serializable @Serializable
@ -48,6 +49,6 @@ public class VisionOfRangeField(
public val label: String? = null, public val label: String? = null,
public val name: String? = null, public val name: String? = null,
) : VisionOfHtmlInput() { ) : VisionOfHtmlInput() {
public var value: Number? by meta.number() public var value: Number? by properties().number()
} }

View File

@ -49,7 +49,7 @@ public fun Vision.propertyValue(
getPropertyValue(name ?: Name.parse(property.name), inherit, includeStyles, includeDefaults) getPropertyValue(name ?: Name.parse(property.name), inherit, includeStyles, includeDefaults)
override fun setValue(thisRef: Any?, property: KProperty<*>, value: Value?) { 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 <T> Vision.propertyValue(
).let(getter) ).let(getter)
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { 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))
} }
} }

View File

@ -3,6 +3,7 @@ package space.kscience.visionforge
import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.*
import space.kscience.dataforge.meta.descriptors.* import space.kscience.dataforge.meta.descriptors.*
import space.kscience.dataforge.values.asValue import space.kscience.dataforge.values.asValue
import space.kscience.dataforge.values.set
private const val INHERITED_DESCRIPTOR_ATTRIBUTE = "inherited" private const val INHERITED_DESCRIPTOR_ATTRIBUTE = "inherited"
private const val STYLE_DESCRIPTOR_ATTRIBUTE = "useStyles" private const val STYLE_DESCRIPTOR_ATTRIBUTE = "useStyles"

View File

@ -1,6 +1,5 @@
package space.kscience.visionforge.visitor package space.kscience.visionforge.visitor
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
@ -11,7 +10,6 @@ import space.kscience.visionforge.Vision
import kotlin.reflect.KClass import kotlin.reflect.KClass
@OptIn(ExperimentalCoroutinesApi::class)
public suspend fun <T> Vision.flowStatistics(statistics: (Name, Vision) -> T): Flow<T> = callbackFlow<T> { public suspend fun <T> Vision.flowStatistics(statistics: (Name, Vision) -> T): Flow<T> = callbackFlow<T> {
val visitor = object : VisionVisitor { val visitor = object : VisionVisitor {
override suspend fun visit(name: Name, vision: Vision){ override suspend fun visit(name: Name, vision: Vision){

View File

@ -6,7 +6,7 @@ import kotlinx.coroutines.launch
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.plus import space.kscience.dataforge.names.plus
import space.kscience.visionforge.Vision import space.kscience.visionforge.Vision
import space.kscience.visionforge.VisionGroup import space.kscience.visionforge.iterator
public interface VisionVisitor { public interface VisionVisitor {
/** /**
@ -19,30 +19,30 @@ public interface VisionVisitor {
/** /**
* Rearrange children of given group * 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 //Do nothing by default
} }
public fun skip(name: Name, vision: Vision): Boolean = false public fun skip(name: Name, vision: Vision): Boolean = false
public companion object{ public companion object {
private fun CoroutineScope.visitTreeAsync( private fun CoroutineScope.visitTreeAsync(
visionVisitor: VisionVisitor, visionVisitor: VisionVisitor,
name: Name, name: Name,
vision: Vision vision: Vision,
): Job = launch { ): Job = launch {
if (visionVisitor.skip(name, vision)) return@launch if (visionVisitor.skip(name, vision)) return@launch
visionVisitor.visit(name, vision) visionVisitor.visit(name, vision)
if (vision is VisionGroup) {
visionVisitor.visitChildren(name, vision)
for ((token, child) in vision.children) { visionVisitor.visitChildren(name, vision)
visitTreeAsync(visionVisitor, name + token, child)
} for ((token, child) in vision.children) {
visitTreeAsync(visionVisitor, name + token, child)
} }
} }
/** /**
* Recursively visit this [Vision] and all children * Recursively visit this [Vision] and all children
*/ */

View File

@ -4,13 +4,9 @@ import kotlinx.html.*
import kotlinx.html.stream.createHTML import kotlinx.html.stream.createHTML
import space.kscience.dataforge.context.Global import space.kscience.dataforge.context.Global
import space.kscience.dataforge.meta.Meta 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.misc.DFExperimental
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.visionforge.Vision import space.kscience.visionforge.*
import space.kscience.visionforge.VisionBase
import space.kscience.visionforge.VisionManager
import kotlin.collections.set import kotlin.collections.set
import kotlin.test.Test import kotlin.test.Test
@ -36,7 +32,7 @@ fun FlowContent.renderVisionFragment(
@DFExperimental @DFExperimental
class HtmlTagTest { class HtmlTagTest {
fun VisionOutput.base(block: VisionBase.() -> Unit) = VisionBase().apply(block) fun VisionOutput.base(block: VisionGroup.() -> Unit) = VisionGroup().apply(block)
val fragment: HtmlVisionFragment = { val fragment: HtmlVisionFragment = {
div { div {
@ -46,10 +42,8 @@ class HtmlTagTest {
"metaProperty" put 87 "metaProperty" put 87
} }
base { base {
configure { setPropertyValue("myProp", 82)
set("myProp", 82) setPropertyValue("otherProp", false)
set("otherProp", false)
}
} }
} }
} }
@ -59,7 +53,7 @@ class HtmlTagTest {
div { div {
h2 { +"Properties" } h2 { +"Properties" }
ul { ul {
(vision as? VisionBase)?.meta?.items?.forEach { vision.getProperty(Name.EMPTY).items.forEach {
li { li {
a { +it.key.toString() } a { +it.key.toString() }
p { +it.value.toString() } p { +it.value.toString() }

View File

@ -1,8 +1,16 @@
package space.kscience.visionforge.meta 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.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.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertNotEquals import kotlin.test.assertNotEquals
@ -10,22 +18,22 @@ import kotlin.test.assertNotEquals
class VisionPropertyTest { class VisionPropertyTest {
@Test @Test
fun testPropertyWrite(){ fun testPropertyWrite(){
val vision = VisionBase() val vision = VisionGroup()
vision.meta["fff"] = 2 vision.setPropertyValue("fff", 2)
vision.meta["fff.ddd"] = false vision.setPropertyValue("fff.ddd", false)
assertEquals(2, vision.meta["fff"]?.int) assertEquals(2, vision.getPropertyValue("fff")?.int)
assertEquals(false, vision.meta["fff.ddd"]?.boolean) assertEquals(false, vision.getPropertyValue("fff.ddd")?.boolean)
} }
@Test @Test
fun testPropertyEdit(){ fun testPropertyEdit(){
val vision = VisionBase() val vision = VisionGroup()
vision.meta.getOrCreate("fff.ddd").apply { vision.getProperty("fff.ddd").apply {
value = 2.asValue() value = 2.asValue()
} }
assertEquals(2, vision.meta["fff.ddd"]?.int) assertEquals(2, vision.getPropertyValue("fff.ddd")?.int)
assertNotEquals(true, vision.meta["fff.ddd"]?.boolean) assertNotEquals(true, vision.getPropertyValue("fff.ddd")?.boolean)
} }
internal class TestScheme: Scheme(){ internal class TestScheme: Scheme(){
@ -35,10 +43,10 @@ class VisionPropertyTest {
@Test @Test
fun testPropertyUpdate(){ fun testPropertyUpdate(){
val vision = VisionBase() val vision = VisionGroup()
vision.meta.getOrCreate("fff").updateWith(TestScheme){ vision.getProperty("fff").updateWith(TestScheme){
ddd = 2 ddd = 2
} }
assertEquals(2, vision.meta["fff.ddd"]?.int) assertEquals(2, vision.getPropertyValue("fff.ddd")?.int)
} }
} }

View File

@ -6,11 +6,10 @@ import javafx.scene.Node
import javafx.scene.Parent import javafx.scene.Parent
import javafx.scene.layout.VBox import javafx.scene.layout.VBox
import space.kscience.dataforge.meta.MutableMeta 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.MetaDescriptor
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.visionforge.Vision import space.kscience.visionforge.Vision
import space.kscience.visionforge.computeProperties import space.kscience.visionforge.getProperty
import space.kscience.visionforge.getStyle import space.kscience.visionforge.getStyle
import space.kscience.visionforge.styles import space.kscience.visionforge.styles
import tornadofx.* import tornadofx.*
@ -21,8 +20,8 @@ public class VisionEditorFragment : Fragment() {
public var vision: Vision? by visionProperty public var vision: Vision? by visionProperty
public val descriptorProperty: SimpleObjectProperty<MetaDescriptor> = SimpleObjectProperty<MetaDescriptor>() public val descriptorProperty: SimpleObjectProperty<MetaDescriptor> = SimpleObjectProperty<MetaDescriptor>()
private val configProperty: Binding<ObservableMutableMeta?> = visionProperty.objectBinding { vision -> private val configProperty: Binding<MutableMeta?> = visionProperty.objectBinding { vision ->
vision?.meta vision?.getProperty(Name.EMPTY)
} }
private val configEditorProperty: Binding<Node?> = configProperty.objectBinding(descriptorProperty) { private val configEditorProperty: Binding<Node?> = configProperty.objectBinding(descriptorProperty) {
@ -30,7 +29,7 @@ public class VisionEditorFragment : Fragment() {
val node:FXMetaModel<MutableMeta> = FXMetaModel( val node:FXMetaModel<MutableMeta> = FXMetaModel(
meta, meta,
vision?.descriptor, vision?.descriptor,
vision?.computeProperties(), vision?.meta,
Name.EMPTY, Name.EMPTY,
"Vision properties" "Vision properties"
) )

View File

@ -5,17 +5,17 @@ import javafx.scene.control.SelectionMode
import javafx.scene.control.TreeItem import javafx.scene.control.TreeItem
import javafx.scene.layout.VBox import javafx.scene.layout.VBox
import space.kscience.visionforge.Vision import space.kscience.visionforge.Vision
import space.kscience.visionforge.VisionGroup import space.kscience.visionforge.solid.SolidGroup
import tornadofx.* import tornadofx.*
private fun toTreeItem(vision: Vision, title: String): TreeItem<Pair<String, Vision>> { private fun toTreeItem(vision: Vision, title: String): TreeItem<Pair<String, Vision>> {
return object : TreeItem<Pair<String, Vision>>(title to vision) { return object : TreeItem<Pair<String, Vision>>(title to vision) {
init { init {
if (vision is VisionGroup) { if (vision is SolidGroup) {
//lazy populate the tree //lazy populate the tree
expandedProperty().onChange { expanded -> expandedProperty().onChange { expanded ->
if (expanded && children.isEmpty()) { if (expanded && children.isEmpty()) {
children.setAll(vision.children.map { children.setAll(vision.items.map {
toTreeItem(it.value, it.key.toString()) toTreeItem(it.value, it.key.toString())
}) })
} }
@ -24,7 +24,7 @@ private fun toTreeItem(vision: Vision, title: String): TreeItem<Pair<String, Vis
} }
override fun isLeaf(): Boolean { override fun isLeaf(): Boolean {
return !(vision is VisionGroup && vision.children.isNotEmpty()) return !(vision is SolidGroup && vision.items.isNotEmpty())
} }
} }
} }

View File

@ -16,7 +16,7 @@ import space.kscience.dataforge.context.*
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.boolean import space.kscience.dataforge.meta.boolean
import space.kscience.dataforge.misc.Type import space.kscience.dataforge.misc.Type
import space.kscience.visionforge.computePropertyNode import space.kscience.visionforge.getProperty
import space.kscience.visionforge.solid.FX3DFactory.Companion.TYPE import space.kscience.visionforge.solid.FX3DFactory.Companion.TYPE
import space.kscience.visionforge.solid.SolidMaterial.Companion.MATERIAL_KEY import space.kscience.visionforge.solid.SolidMaterial.Companion.MATERIAL_KEY
import space.kscience.visionforge.solid.SolidMaterial.Companion.MATERIAL_WIREFRAME_KEY import space.kscience.visionforge.solid.SolidMaterial.Companion.MATERIAL_WIREFRAME_KEY
@ -48,9 +48,9 @@ public class FX3DPlugin : AbstractPlugin() {
public fun buildNode(obj: Solid): Node { public fun buildNode(obj: Solid): Node {
val binding = VisualObjectFXBinding(this, obj) val binding = VisualObjectFXBinding(this, obj)
return when (obj) { return when (obj) {
is SolidReferenceGroup -> referenceFactory(obj, binding) is SolidReference -> referenceFactory(obj, binding)
is SolidGroup -> { is SolidGroup -> {
Group(obj.children.mapNotNull { (token, obj) -> Group(obj.items.mapNotNull { (token, obj) ->
(obj as? Solid)?.let { (obj as? Solid)?.let {
logger.info { token.toString() } logger.info { token.toString() }
buildNode(it).apply { buildNode(it).apply {
@ -77,7 +77,7 @@ public class FX3DPlugin : AbstractPlugin() {
is PolyLine -> PolyLine3D( is PolyLine -> PolyLine3D(
obj.points.map { Point3D(it.x, it.y, it.z) }, obj.points.map { Point3D(it.x, it.y, it.z) },
obj.thickness.toFloat(), obj.thickness.toFloat(),
obj.computePropertyNode(SolidMaterial.MATERIAL_COLOR_KEY)?.color() obj.getProperty(SolidMaterial.MATERIAL_COLOR_KEY).color()
).apply { ).apply {
this.meshView.cullFace = CullFace.FRONT this.meshView.cullFace = CullFace.FRONT
} }

View File

@ -8,20 +8,21 @@ import space.kscience.dataforge.names.firstOrNull
import space.kscience.dataforge.names.isEmpty import space.kscience.dataforge.names.isEmpty
import space.kscience.visionforge.Vision import space.kscience.visionforge.Vision
import space.kscience.visionforge.onPropertyChange import space.kscience.visionforge.onPropertyChange
import space.kscience.visionforge.solid.SolidReference.Companion.REFERENCE_CHILD_PROPERTY_PREFIX
import kotlin.reflect.KClass import kotlin.reflect.KClass
public class FXReferenceFactory(public val plugin: FX3DPlugin) : FX3DFactory<SolidReferenceGroup> { public class FXReferenceFactory(public val plugin: FX3DPlugin) : FX3DFactory<SolidReference> {
override val type: KClass<in SolidReferenceGroup> get() = SolidReferenceGroup::class override val type: KClass<in SolidReference> get() = SolidReference::class
override fun invoke(obj: SolidReferenceGroup, binding: VisualObjectFXBinding): Node { override fun invoke(obj: SolidReference, binding: VisualObjectFXBinding): Node {
val prototype = obj.prototype val prototype = obj.prototype
val node = plugin.buildNode(prototype) val node = plugin.buildNode(prototype)
obj.onPropertyChange { name-> 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 childName = name.firstOrNull()?.index?.let(Name::parse) ?: error("Wrong syntax for reference child property: '$name'")
val propertyName = name.cutFirst() val propertyName = name.cutFirst()
val referenceChild = obj[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") val child = node.findChild(childName) ?: error("Object child with name '$childName' not found")
child.updateProperty(referenceChild, propertyName) child.updateProperty(referenceChild, propertyName)
} }

View File

@ -7,7 +7,7 @@ import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.startsWith import space.kscience.dataforge.names.startsWith
import space.kscience.dataforge.values.Value import space.kscience.dataforge.values.Value
import space.kscience.visionforge.Vision import space.kscience.visionforge.Vision
import space.kscience.visionforge.computePropertyNode import space.kscience.visionforge.getProperty
import space.kscience.visionforge.onPropertyChange import space.kscience.visionforge.onPropertyChange
import tornadofx.* import tornadofx.*
@ -36,7 +36,7 @@ public class VisualObjectFXBinding(public val fx: FX3DPlugin, public val obj: Vi
public operator fun get(key: Name): ObjectBinding<Meta?> { public operator fun get(key: Name): ObjectBinding<Meta?> {
return bindings.getOrPut(key) { return bindings.getOrPut(key) {
object : ObjectBinding<Meta?>() { object : ObjectBinding<Meta?>() {
override fun computeValue(): Meta? = obj.computePropertyNode(key) override fun computeValue(): Meta = obj.getProperty(key)
} }
} }
} }

View File

@ -6,7 +6,7 @@ import space.kscience.dataforge.names.Name
import space.kscience.gdml.* import space.kscience.gdml.*
import space.kscience.visionforge.solid.Solid import space.kscience.visionforge.solid.Solid
import space.kscience.visionforge.solid.SolidMaterial import space.kscience.visionforge.solid.SolidMaterial
import space.kscience.visionforge.solid.invoke import space.kscience.visionforge.solid.set
import space.kscience.visionforge.useStyle import space.kscience.visionforge.useStyle
import kotlin.random.Random import kotlin.random.Random
@ -44,7 +44,7 @@ public class GdmlLoaderOptions {
* Configure paint for given solid with given [GdmlMaterial] * Configure paint for given solid with given [GdmlMaterial]
*/ */
public var configurePaint: SolidMaterial.(material: GdmlMaterial, solid: GdmlSolid) -> Unit = public var configurePaint: SolidMaterial.(material: GdmlMaterial, solid: GdmlSolid) -> Unit =
{ material, _ -> color(randomColor(material)) } { material, _ -> color.set(randomColor(material)) }
private set private set
public fun paint(block: SolidMaterial.(material: GdmlMaterial, solid: GdmlSolid) -> Unit) { public fun paint(block: SolidMaterial.(material: GdmlMaterial, solid: GdmlSolid) -> Unit) {

View File

@ -30,10 +30,10 @@ private class GdmlLoader(val settings: GdmlLoaderOptions) {
private val proto = SolidGroup() private val proto = SolidGroup()
private val solids = proto.group(solidsName) { private val solids = proto.group(solidsName) {
setPropertyNode("edges.enabled", false) setPropertyValue("edges.enabled", false)
} }
private val referenceStore = HashMap<Name, MutableList<SolidReferenceGroup>>() private val referenceStore = HashMap<Name, MutableList<SolidReference>>()
fun Solid.configureSolid(root: Gdml, parent: GdmlVolume, solid: GdmlSolid) { fun Solid.configureSolid(root: Gdml, parent: GdmlVolume, solid: GdmlSolid) {
val material = parent.materialref.resolve(root) ?: GdmlElement(parent.materialref.ref) 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 val templateName = solidsName + name
if (proto[templateName] == null) { if (proto[templateName] == null) {
solids.addSolid(root, solid, name) solids.addSolid(root, solid, name)
@ -59,7 +59,7 @@ private class GdmlLoader(val settings: GdmlLoaderOptions) {
group: SolidGroup, group: SolidGroup,
physVolume: GdmlPhysVolume, physVolume: GdmlPhysVolume,
volume: GdmlGroup, volume: GdmlGroup,
): SolidReferenceGroup { ): SolidReference {
val templateName = volumesName + volume.name.asName() val templateName = volumesName + volume.name.asName()
if (proto[templateName] == null) { if (proto[templateName] == null) {
proto[templateName] = volume(root, volume) proto[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") ?: error("Volume with ref ${divisionVolume.volumeref.ref} could not be resolved")
//TODO add divisions //TODO add divisions
set(null, volume(root, volume)) children.static(volume(root, volume))
} }
private fun volume( private fun volume(
@ -355,7 +355,7 @@ private class GdmlLoader(val settings: GdmlLoaderOptions) {
final.useStyle(rootStyle) final.useStyle(rootStyle)
final.prototypes { final.prototypes {
proto.children.forEach { (token, item) -> proto.items.forEach { (token, item) ->
item.parent = null item.parent = null
set(token.asName(), item as? Solid) set(token.asName(), item as? Solid)
} }
@ -383,9 +383,9 @@ public fun Gdml.toVision(block: GdmlLoaderOptions.() -> Unit = {}): SolidGroup {
* Append Gdml node to the group * Append Gdml node to the group
*/ */
public fun SolidGroup.gdml(gdml: Gdml, key: String? = null, transformer: GdmlLoaderOptions.() -> Unit = {}) { public fun SolidGroup.gdml(gdml: Gdml, key: String? = null, transformer: GdmlLoaderOptions.() -> Unit = {}) {
val visual = gdml.toVision(transformer) val vision = gdml.toVision(transformer)
//println(Visual3DPlugin.json.stringify(VisualGroup3D.serializer(), visual)) //println(Visual3DPlugin.json.stringify(VisualGroup3D.serializer(), visual))
set(key, visual) children[key] = vision
} }
@VisionBuilder @VisionBuilder

View File

@ -6,10 +6,9 @@ import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.NameToken import space.kscience.dataforge.names.NameToken
import space.kscience.dataforge.names.length import space.kscience.dataforge.names.length
import space.kscience.dataforge.names.plus import space.kscience.dataforge.names.plus
import space.kscience.visionforge.VisionGroup
import space.kscience.visionforge.solid.Solid import space.kscience.visionforge.solid.Solid
import space.kscience.visionforge.solid.SolidGroup import space.kscience.visionforge.solid.SolidGroup
import space.kscience.visionforge.solid.SolidReferenceGroup import space.kscience.visionforge.solid.SolidReference
import space.kscience.visionforge.solid.layer import space.kscience.visionforge.solid.layer
@ -23,15 +22,15 @@ private class VisionCounterTree(
var selfCount = 1 var selfCount = 1
val children: Map<NameToken, VisionCounterTree> by lazy { val children: Map<NameToken, VisionCounterTree> by lazy {
(vision as? VisionGroup)?.children?.mapValues { (key, vision) -> (vision as? SolidGroup)?.items?.mapValues { (key, vision) ->
if (vision is SolidReferenceGroup) { if (vision is SolidReference) {
prototypes.getOrPut(vision.refName) { prototypes.getOrPut(vision.prototypeName) {
VisionCounterTree(vision.refName, vision.prototype, prototypes) VisionCounterTree(vision.prototypeName, vision.prototype, prototypes)
}.apply { }.apply {
selfCount += 1 selfCount += 1
} }
} else { } else {
VisionCounterTree(name + key, vision as Solid, prototypes) VisionCounterTree(name + key, vision, prototypes)
} }
} ?: emptyMap() } ?: emptyMap()
} }
@ -51,10 +50,10 @@ private fun VisionCounterTree.topToBottom(): Sequence<VisionCounterTree> = seque
} }
public fun SolidGroup.markLayers(thresholds: List<Int> = listOf(500, 1000, 20000, 50000)) { public fun SolidGroup.markLayers(thresholds: List<Int> = listOf(500, 1000, 20000, 50000)) {
val logger = manager?.context?.logger val logger = manager.context.logger
val counterTree = VisionCounterTree(Name.EMPTY, this, hashMapOf()) val counterTree = VisionCounterTree(Name.EMPTY, this, hashMapOf())
val totalCount = counterTree.childrenCount val totalCount = counterTree.childrenCount
if (totalCount > thresholds.firstOrNull() ?: 0) { if (totalCount > (thresholds.firstOrNull() ?: 0)) {
val allNodes = counterTree.topToBottom().distinct().toMutableList() val allNodes = counterTree.topToBottom().distinct().toMutableList()
//println("tree construction finished") //println("tree construction finished")
allNodes.sortWith( allNodes.sortWith(

View File

@ -21,12 +21,12 @@ class TestCubes {
@Test @Test
fun testCubesDirect() { fun testCubesDirect() {
val vision = cubes.toVision() val vision: SolidGroup = cubes.toVision()
// println(Solids.encodeToString(vision)) // println(Solids.encodeToString(vision))
val smallBoxPrototype = vision.getPrototype(Name.parse("solids.smallBox")) as? Box val smallBoxPrototype = vision.getPrototype(Name.parse("solids.smallBox")) as? Box
assertNotNull(smallBoxPrototype) assertNotNull(smallBoxPrototype)
assertEquals(30.0, smallBoxPrototype.xSize.toDouble()) assertEquals(30.0, smallBoxPrototype.xSize.toDouble())
val smallBoxVision = vision["composite-111.smallBox"]?.unref as? Box val smallBoxVision = vision.children["composite-111.smallBox"]?.unref as? Box
assertNotNull(smallBoxVision) assertNotNull(smallBoxVision)
assertEquals(30.0, smallBoxVision.xSize.toDouble()) assertEquals(30.0, smallBoxVision.xSize.toDouble())
} }
@ -55,7 +55,7 @@ class TestCubes {
assertNotNull(this.prototype) assertNotNull(this.prototype)
} }
if (this is SolidGroup) { if (this is SolidGroup) {
children.forEach { items.forEach {
it.value.checkPrototypes() it.value.checkPrototypes()
} }
} }

View File

@ -9,17 +9,18 @@ import space.kscience.dataforge.meta.string
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.asName
import space.kscience.visionforge.Vision import space.kscience.visionforge.Vision
import space.kscience.visionforge.VisionBase import space.kscience.visionforge.VisionGroup
import space.kscience.visionforge.properties
@Serializable @Serializable
@SerialName("vision.markup") @SerialName("vision.markup")
public class VisionOfMarkup( public class VisionOfMarkup(
public val format: String = COMMONMARK_FORMAT public val format: String = COMMONMARK_FORMAT
) : VisionBase() { ) : VisionGroup() {
//TODO add templates //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 companion object {
public val CONTENT_PROPERTY_KEY: Name = "content".asName() public val CONTENT_PROPERTY_KEY: Name = "content".asName()

View File

@ -2,21 +2,24 @@ package space.kscience.visionforge.plotly
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import space.kscience.dataforge.meta.asObservable
import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.names.Name
import space.kscience.plotly.Plot import space.kscience.plotly.Plot
import space.kscience.plotly.Plotly 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 import space.kscience.visionforge.html.VisionOutput
@Serializable @Serializable
@SerialName("vision.plotly") @SerialName("vision.plotly")
public class VisionOfPlotly private constructor() : VisionBase() { public class VisionOfPlotly private constructor() : VisionGroup() {
public constructor(plot: Plot) : this() { 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) public fun Plot.asVision(): VisionOfPlotly = VisionOfPlotly(this)

View File

@ -8,7 +8,7 @@ import io.ktor.server.engine.embeddedServer
import io.ktor.server.html.respondHtml import io.ktor.server.html.respondHtml
import io.ktor.server.http.content.resources import io.ktor.server.http.content.resources
import io.ktor.server.http.content.static 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.respond
import io.ktor.server.response.respondText import io.ktor.server.response.respondText
import io.ktor.server.routing.* import io.ktor.server.routing.*
@ -18,7 +18,6 @@ import io.ktor.server.websocket.webSocket
import io.ktor.websocket.Frame import io.ktor.websocket.Frame
import kotlinx.coroutines.channels.consumeEach import kotlinx.coroutines.channels.consumeEach
import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext

View File

@ -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 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; public abstract fun getPrototype ()Lspace/kscience/visionforge/solid/Solid;
} }
@ -728,7 +728,7 @@ public final class space/kscience/visionforge/solid/SolidReferenceGroup : space/
public fun <init> (Lspace/kscience/dataforge/names/Name;)V public fun <init> (Lspace/kscience/dataforge/names/Name;)V
public fun getChildren ()Ljava/util/Map; public fun getChildren ()Ljava/util/Map;
public fun getDescriptor ()Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor; 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 fun getPrototype ()Lspace/kscience/visionforge/solid/Solid;
public final fun getRefName ()Lspace/kscience/dataforge/names/Name; 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 public static final fun write$Self (Lspace/kscience/visionforge/solid/SolidReferenceGroup;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V

View File

@ -1,12 +1,13 @@
package space.kscience.visionforge.solid package space.kscience.visionforge.solid
import space.kscience.dataforge.meta.Configurable
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.asName
import space.kscience.dataforge.names.plus import space.kscience.dataforge.names.plus
import space.kscience.dataforge.values.* import space.kscience.dataforge.values.*
import space.kscience.visionforge.Colors import space.kscience.visionforge.Colors
import space.kscience.visionforge.Vision
import space.kscience.visionforge.VisionBuilder import space.kscience.visionforge.VisionBuilder
import space.kscience.visionforge.getProperty
import kotlin.properties.ReadOnlyProperty import kotlin.properties.ReadOnlyProperty
@VisionBuilder @VisionBuilder
@ -27,8 +28,8 @@ public class ColorAccessor(
} }
} }
public fun Configurable.color(): ReadOnlyProperty<Configurable, ColorAccessor> = ReadOnlyProperty { _, property -> public fun Vision.color(): ReadOnlyProperty<Vision, ColorAccessor> = ReadOnlyProperty { _, property ->
ColorAccessor(meta, property.name.asName()) ColorAccessor(getProperty(Name.EMPTY), property.name.asName())
} }
public var ColorAccessor?.string: String? 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 * 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() this?.value = webColor.asValue()
} }
/** /**
* Set color as RGB integer * Set color as RGB integer
*/ */
public operator fun ColorAccessor?.invoke(rgb: Int) { public fun ColorAccessor?.set(rgb: Int) {
this?.value = Colors.rgbToString(rgb).asValue() this?.value = Colors.rgbToString(rgb).asValue()
} }
/** /**
* Set color as RGB * 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() this?.value = Colors.rgbToString(r, g, b).asValue()
} }

View File

@ -3,11 +3,8 @@ package space.kscience.visionforge.solid
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import space.kscience.dataforge.meta.isEmpty import space.kscience.dataforge.meta.isEmpty
import space.kscience.dataforge.meta.update import space.kscience.dataforge.names.Name
import space.kscience.visionforge.VisionBuilder import space.kscience.visionforge.*
import space.kscience.visionforge.VisionContainerBuilder
import space.kscience.visionforge.VisionPropertyContainer
import space.kscience.visionforge.set
public enum class CompositeType { public enum class CompositeType {
GROUP, // Dumb sum of meshes GROUP, // Dumb sum of meshes
@ -28,16 +25,16 @@ public class Composite(
public inline fun VisionContainerBuilder<Solid>.composite( public inline fun VisionContainerBuilder<Solid>.composite(
type: CompositeType, type: CompositeType,
name: String? = null, name: String? = null,
builder: SolidGroup.() -> Unit, @VisionBuilder builder: SolidGroup.() -> Unit,
): Composite { ): Composite {
val group = SolidGroup().apply(builder) val group = SolidGroup(builder)
val children = group.children.values.filterIsInstance<Solid>() val children = group.items.values.toList()
if (children.size != 2){ if (children.size != 2) {
error("Composite requires exactly two children, but found ${children.size}") error("Composite requires exactly two children, but found ${children.size}")
} }
val res = Composite(type, children[0], children[1]) val res = Composite(type, children[0], children[1])
res.meta.update(group.meta) res.setProperty(Name.EMPTY, group.getProperty(Name.EMPTY))
set(name, res) set(name, res)
return res return res
@ -50,34 +47,34 @@ public inline fun VisionContainerBuilder<Solid>.composite(
public fun SolidGroup.smartComposite( public fun SolidGroup.smartComposite(
type: CompositeType, type: CompositeType,
name: String? = null, name: String? = null,
builder: SolidGroup.() -> Unit, @VisionBuilder builder: SolidGroup.() -> Unit,
): Solid = if (type == CompositeType.GROUP) { ): Solid = if (type == CompositeType.GROUP) {
val group = SolidGroup(builder) val group = SolidGroup(builder)
if (name == null && group.meta.isEmpty()) { if (name == null && group.meta.isEmpty()) {
//append directly to group if no properties are defined //append directly to group if no properties are defined
group.children.forEach { (_, value) -> group.items.forEach { (_, value) ->
value.parent = null value.parent = null
set(null, value) children.static(value)
} }
this this
} else { } else {
set(name, group) children[name] = group
group group
} }
} else { } else {
composite(type, name, builder) children.composite(type, name, builder)
} }
@VisionBuilder @VisionBuilder
public inline fun VisionContainerBuilder<Solid>.union( public inline fun VisionContainerBuilder<Solid>.union(
name: String? = null, name: String? = null,
builder: SolidGroup.() -> Unit builder: SolidGroup.() -> Unit,
): Composite = composite(CompositeType.UNION, name, builder = builder) ): Composite = composite(CompositeType.UNION, name, builder = builder)
@VisionBuilder @VisionBuilder
public inline fun VisionContainerBuilder<Solid>.subtract( public inline fun VisionContainerBuilder<Solid>.subtract(
name: String? = null, name: String? = null,
builder: SolidGroup.() -> Unit builder: SolidGroup.() -> Unit,
): Composite = composite(CompositeType.SUBTRACT, name, builder = builder) ): Composite = composite(CompositeType.SUBTRACT, name, builder = builder)
@VisionBuilder @VisionBuilder

View File

@ -4,7 +4,7 @@ import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import space.kscience.dataforge.meta.MutableMeta import space.kscience.dataforge.meta.MutableMeta
import space.kscience.dataforge.meta.ObservableMutableMeta import space.kscience.dataforge.meta.ObservableMutableMeta
import space.kscience.dataforge.meta.configure import space.kscience.dataforge.names.Name
import space.kscience.visionforge.* import space.kscience.visionforge.*
import kotlin.math.PI import kotlin.math.PI
import kotlin.math.cos 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") @SerialName("solid.extrude")
public class Extruded( public class Extruded(
public val shape: List<Point2D>, public val shape: List<Point2D>,
public val layers: List<Layer> public val layers: List<Layer>,
) : SolidBase(), GeometrySolid, VisionPropertyContainer<Extruded> { ) : SolidBase(), GeometrySolid, VisionPropertyContainer<Extruded> {
override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) { override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) {
@ -67,7 +67,7 @@ public class Extruded(
for (i in (1 until layers.size)) { for (i in (1 until layers.size)) {
upperLayer = layers[i] upperLayer = layers[i]
for (j in (0 until shape.size - 1)) { for (j in (0 until shape.size - 1)) {
//counter clockwise //counterclockwise
geometryBuilder.face4( geometryBuilder.face4(
lowerLayer[j], lowerLayer[j],
lowerLayer[j + 1], lowerLayer[j + 1],
@ -99,7 +99,7 @@ public class ExtrudeBuilder(
public var layers: MutableList<Layer> = ArrayList(), public var layers: MutableList<Layer> = ArrayList(),
config: ObservableMutableMeta = MutableMeta() config: ObservableMutableMeta = MutableMeta(),
) : SimpleVisionPropertyContainer<Extruded>(config) { ) : SimpleVisionPropertyContainer<Extruded>(config) {
public fun shape(block: Shape2DBuilder.() -> Unit) { public fun shape(block: Shape2DBuilder.() -> Unit) {
this.shape = Shape2DBuilder().apply(block).build() this.shape = Shape2DBuilder().apply(block).build()
@ -110,12 +110,12 @@ public class ExtrudeBuilder(
} }
internal fun build(): Extruded = Extruded(shape, layers).apply { internal fun build(): Extruded = Extruded(shape, layers).apply {
configure(this@ExtrudeBuilder.meta) setProperty(Name.EMPTY, getProperty(Name.EMPTY))
} }
} }
@VisionBuilder @VisionBuilder
public fun VisionContainerBuilder<Solid>.extruded( public fun VisionContainerBuilder<Solid>.extruded(
name: String? = null, name: String? = null,
action: ExtrudeBuilder.() -> Unit = {} action: ExtrudeBuilder.() -> Unit = {},
): Extruded = ExtrudeBuilder().apply(action).build().also { set(name, it) } ): Extruded = ExtrudeBuilder().apply(action).build().also { set(name, it) }

View File

@ -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]

View File

@ -6,16 +6,12 @@ import space.kscience.dataforge.meta.descriptors.node
import space.kscience.dataforge.meta.descriptors.value import space.kscience.dataforge.meta.descriptors.value
import space.kscience.dataforge.meta.float import space.kscience.dataforge.meta.float
import space.kscience.dataforge.meta.get import space.kscience.dataforge.meta.get
import space.kscience.dataforge.meta.number
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.asName
import space.kscience.dataforge.names.plus import space.kscience.dataforge.names.plus
import space.kscience.dataforge.values.* 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.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.DETAIL_KEY
import space.kscience.visionforge.solid.Solid.Companion.IGNORE_KEY import space.kscience.visionforge.solid.Solid.Companion.IGNORE_KEY
import space.kscience.visionforge.solid.Solid.Companion.LAYER_KEY import space.kscience.visionforge.solid.Solid.Companion.LAYER_KEY
@ -38,7 +34,7 @@ import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
/** /**
* Interface for 3-dimensional [Vision] * Interface for a [Vision] representing a 3D object
*/ */
public interface Solid : Vision { public interface Solid : Vision {
@ -121,7 +117,7 @@ public interface Solid : Vision {
public var Solid.layer: Int public var Solid.layer: Int
get() = getPropertyValue(LAYER_KEY, inherit = true)?.int ?: 0 get() = getPropertyValue(LAYER_KEY, inherit = true)?.int ?: 0
set(value) { set(value) {
setProperty(LAYER_KEY, value) setPropertyValue(LAYER_KEY, value)
} }
// Common properties // Common properties
@ -140,23 +136,23 @@ public enum class RotationOrder {
*/ */
public var Solid.rotationOrder: RotationOrder public var Solid.rotationOrder: RotationOrder
get() = getPropertyValue(Solid.ROTATION_ORDER_KEY)?.enum<RotationOrder>() ?: RotationOrder.XYZ get() = getPropertyValue(Solid.ROTATION_ORDER_KEY)?.enum<RotationOrder>() ?: 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 * Preferred number of polygons for displaying the object. If not defined, uses shape or renderer default. Not inherited
*/ */
public var Solid.detail: Int? public var Solid.detail: Int?
get() = getPropertyValue(DETAIL_KEY, false)?.int get() = getPropertyValue(DETAIL_KEY, inherit = false)?.int
set(value) = meta.setValue(DETAIL_KEY, value?.asValue()) set(value) = setPropertyValue(DETAIL_KEY, value?.asValue())
/** /**
* If this property is true, the object will be ignored on render. * If this property is true, the object will be ignored on render.
* Property is not inherited. * Property is not inherited.
*/ */
public var Vision.ignore: Boolean? public var Vision.ignore: Boolean?
get() = getPropertyValue(IGNORE_KEY, false)?.boolean get() = getPropertyValue(IGNORE_KEY, inherit = false)?.boolean
set(value) = meta.setValue(IGNORE_KEY, value?.asValue()) set(value) = setPropertyValue(IGNORE_KEY, value?.asValue())
//var VisualObject.selected: Boolean? //var VisualObject.selected: Boolean?
// get() = getProperty(SELECTED_KEY).boolean // get() = getProperty(SELECTED_KEY).boolean
@ -165,18 +161,18 @@ public var Vision.ignore: Boolean?
internal fun float(name: Name, default: Number): ReadWriteProperty<Solid, Number> = internal fun float(name: Name, default: Number): ReadWriteProperty<Solid, Number> =
object : ReadWriteProperty<Solid, Number> { object : ReadWriteProperty<Solid, Number> {
override fun getValue(thisRef: Solid, property: KProperty<*>): Number { 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) { 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<Solid, Point3D?> = internal fun point(name: Name, default: Float): ReadWriteProperty<Solid, Point3D?> =
object : ReadWriteProperty<Solid, Point3D?> { object : ReadWriteProperty<Solid, Point3D?> {
override fun getValue(thisRef: Solid, property: KProperty<*>): Point3D? { 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 { return object : Point3D {
override val x: Float get() = item[X_KEY]?.float ?: default override val x: Float get() = item[X_KEY]?.float ?: default
override val y: Float get() = item[Y_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<Solid, Point3D
override fun setValue(thisRef: Solid, property: KProperty<*>, value: Point3D?) { override fun setValue(thisRef: Solid, property: KProperty<*>, value: Point3D?) {
if (value == null) { if (value == null) {
thisRef.meta.setMeta(name, null) thisRef.setProperty(name, null)
} else { } else {
thisRef.setProperty(name + X_KEY, value.x) thisRef.setPropertyValue(name + X_KEY, value.x)
thisRef.setProperty(name + Y_KEY, value.y) thisRef.setPropertyValue(name + Y_KEY, value.y)
thisRef.setProperty(name + Z_KEY, value.z) thisRef.setPropertyValue(name + Z_KEY, value.z)
} }
} }
} }

View File

@ -2,11 +2,24 @@ package space.kscience.visionforge.solid
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import space.kscience.dataforge.meta.MutableMeta
import space.kscience.dataforge.meta.descriptors.MetaDescriptor 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 @Serializable
@SerialName("solid") @SerialName("solid")
public open class SolidBase : VisionBase(), Solid { public open class SolidBase : AbstractVision(), Solid {
override val descriptor: MetaDescriptor get() = Solid.descriptor 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<AbstractVision>.getProperty(name, inherit, includeStyles, includeDefaults)
}
} }

View File

@ -7,6 +7,7 @@ import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.NameToken import space.kscience.dataforge.names.NameToken
import space.kscience.visionforge.* import space.kscience.visionforge.*
/** /**
* A container with prototype support * A container with prototype support
*/ */
@ -23,20 +24,26 @@ public interface PrototypeHolder {
public fun getPrototype(name: Name): Solid? public fun getPrototype(name: Name): Solid?
} }
/** /**
* Represents 3-dimensional Visual Group * A [Solid] group with additional accessor methods
* @param prototypes A container for templates visible inside this group
*/ */
@Serializable @Serializable
@SerialName("group.solid") @SerialName("group.solid")
public class SolidGroup : VisionGroupBase(), Solid, PrototypeHolder { public class SolidGroup : VisionGroup(), Solid, PrototypeHolder, MutableVisionGroup, VisionContainerBuilder<Solid> {
override val children: Map<NameToken, Vision> get() = super.childrenInternal.filter { it.key != PROTOTYPES_TOKEN } public val items: Map<NameToken, Solid>
get() = children.keys.mapNotNull {
val value = children[it] as? Solid ?: return@mapNotNull null
it to value
}.toMap()
private var prototypes: MutableVisionGroup? public operator fun get(name: Name): Solid? = children[name] as? Solid
get() = childrenInternal[PROTOTYPES_TOKEN] as? MutableVisionGroup
private var prototypes: SolidGroup?
get() = items[PROTOTYPES_TOKEN] as? SolidGroup
set(value) { 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 * Create or edit prototype node as a group
*/ */
override fun prototypes(builder: VisionContainerBuilder<Solid>.() -> Unit): Unit { override fun prototypes(builder: VisionContainerBuilder<Solid>.() -> Unit): Unit {
(prototypes ?: SolidGroup().also { (prototypes ?: SolidGroup().also { prototypes = it }).children.run(builder)
prototypes = it
}).run(builder)
} }
override fun createGroup(): SolidGroup = SolidGroup() override fun createGroup(): SolidGroup = SolidGroup()
// //
// override fun update(change: VisionChange) { // override fun update(change: VisionChange) {
// updatePosition(change.properties) // updatePosition(change.properties)
// super.update(change) // super.update(change)
// } // }
override fun set(name: Name?, child: Solid?) {
children[name] = child
}
public companion object { public companion object {
public val PROTOTYPES_TOKEN: NameToken = NameToken("@prototypes") public val PROTOTYPES_TOKEN: NameToken = NameToken("@prototypes")
} }
} }
@Suppress("FunctionName") public inline fun SolidGroup(block: SolidGroup.() -> Unit): SolidGroup = SolidGroup().apply(block)
public fun SolidGroup(block: SolidGroup.() -> Unit): SolidGroup = SolidGroup().apply(block)
@VisionBuilder @VisionBuilder
public fun VisionContainerBuilder<Vision>.group( public fun VisionContainerBuilder<Solid>.group(
name: Name? = null, name: Name? = null,
builder: SolidGroup.() -> Unit = {}, 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. * Define a group with given [name], attach it to this parent and return it.
*/ */
@VisionBuilder @VisionBuilder
public fun VisionContainerBuilder<Vision>.group(name: String, action: SolidGroup.() -> Unit = {}): SolidGroup = public fun VisionContainerBuilder<Solid>.group(
SolidGroup().apply(action).also { set(name, it) } name: String,
action: SolidGroup.() -> Unit = {},
): SolidGroup = SolidGroup(action).also { set(name, it) }

View File

@ -9,6 +9,7 @@ import space.kscience.dataforge.names.plus
import space.kscience.dataforge.values.ValueType import space.kscience.dataforge.values.ValueType
import space.kscience.dataforge.values.asValue import space.kscience.dataforge.values.asValue
import space.kscience.dataforge.values.number import space.kscience.dataforge.values.number
import space.kscience.dataforge.values.set
import space.kscience.visionforge.* import space.kscience.visionforge.*
import space.kscience.visionforge.solid.SolidMaterial.Companion.MATERIAL_COLOR_KEY import space.kscience.visionforge.solid.SolidMaterial.Companion.MATERIAL_COLOR_KEY
import space.kscience.visionforge.solid.SolidMaterial.Companion.MATERIAL_KEY import space.kscience.visionforge.solid.SolidMaterial.Companion.MATERIAL_KEY
@ -101,19 +102,19 @@ public class SolidMaterial : Scheme() {
} }
public val Solid.color: ColorAccessor public val Solid.color: ColorAccessor
get() = ColorAccessor(computePropertyValues(), MATERIAL_COLOR_KEY) get() = ColorAccessor(getProperty(Name.EMPTY), MATERIAL_COLOR_KEY)
public var Solid.material: SolidMaterial? public var Solid.material: SolidMaterial?
get() = computePropertyNode(MATERIAL_KEY)?.let { SolidMaterial.read(it) } get() = SolidMaterial.read(getProperty(MATERIAL_KEY))
set(value) = meta.setMeta(MATERIAL_KEY, value?.meta) set(value) = setProperty(MATERIAL_KEY, value?.meta)
@VisionBuilder @VisionBuilder
public fun Solid.material(builder: SolidMaterial.() -> Unit) { 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? public var Solid.opacity: Number?
get() = getPropertyValue(MATERIAL_OPACITY_KEY, inherit = true)?.number get() = getPropertyValue(MATERIAL_OPACITY_KEY, inherit = true)?.number
set(value) { set(value) {
meta.setValue(MATERIAL_OPACITY_KEY, value?.asValue()) setPropertyValue(MATERIAL_OPACITY_KEY, value?.asValue())
} }

View File

@ -1,38 +1,15 @@
package space.kscience.visionforge.solid 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.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import space.kscience.dataforge.meta.ObservableMutableMeta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.meta.get
import space.kscience.dataforge.names.* import space.kscience.dataforge.names.*
import space.kscience.dataforge.values.Value import space.kscience.dataforge.values.Value
import space.kscience.visionforge.* import space.kscience.visionforge.*
import space.kscience.visionforge.solid.SolidReference.Companion.REFERENCE_CHILD_PROPERTY_PREFIX
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
}
}
/** /**
@ -46,103 +23,116 @@ public val Vision.unref: Solid
else -> error("This Vision is neither Solid nor SolidReference") 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 internal class SolidReferenceChild(
@SerialName("solid.ref") val owner: SolidReference,
public class SolidReferenceGroup( override var parent: Vision?,
public val refName: Name, val childName: Name,
) : VisionBase(), SolidReference, VisionGroup, Solid { ) : Solid {
/** val prototype: Solid
* Recursively search for defined template in the parent get() = owner.prototype.children[childName] as? Solid
*/ ?: error("Prototype with name $childName not found")
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 children: Map<NameToken, Vision> override val meta: Meta get() = owner.getProperty(childToken(childName).asName())
get() = (prototype as? VisionGroup)?.children
?.filter { it.key != SolidGroup.PROTOTYPES_TOKEN }
?.mapValues {
ReferenceChild(this, it.key.asName())
} ?: emptyMap()
override fun getPropertyValue( override fun getPropertyValue(
name: Name, name: Name,
inherit: Boolean, inherit: Boolean,
includeStyles: Boolean, includeStyles: Boolean,
includeDefaults: Boolean includeDefaults: Boolean,
): Value? = super<SolidReference>.getPropertyValue(name, inherit, includeStyles, includeDefaults) ): Value? {
owner.getPropertyValue(
override val descriptor: MetaDescriptor get() = prototype.descriptor childPropertyName(childName, name), inherit, includeStyles, includeDefaults
)?.let { return it }
if (includeStyles) {
/** getStyleProperty(name)?.value?.let { return it }
* 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")
}
} }
prototype.getPropertyValue(name, inherit, includeStyles, includeDefaults)?.let { return it }
override val meta: ObservableMutableMeta by lazy { if (inherit) {
owner.meta.getOrCreate(childToken(refName).asName()) parent?.getPropertyValue(name, inherit, includeStyles, includeDefaults)?.let { return it }
} }
return null
override val children: Map<NameToken, Vision>
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
} }
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<Name>
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<NameToken> get() = prototype.children.keys
override val changes: Flow<Name> 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<NameToken> get() = prototype.children.keys
override val changes: Flow<Name> 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" public const val REFERENCE_CHILD_PROPERTY_PREFIX: String = "@child"
} }
} }
@ -150,33 +140,123 @@ public class SolidReferenceGroup(
/** /**
* Create ref for existing prototype * Create ref for existing prototype
*/ */
public fun SolidGroup.ref( public fun VisionContainerBuilder<Solid>.ref(
templateName: Name, templateName: Name,
name: String? = null, name: String? = null,
): SolidReferenceGroup = SolidReferenceGroup(templateName).also { set(name, it) } ): SolidReference = SolidReference(templateName).also { set(name, it) }
public fun SolidGroup.ref( public fun VisionContainerBuilder<Solid>.ref(
templateName: String, templateName: String,
name: String? = null, 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. * Add new [SolidReference] wrapping given object and automatically adding it to the prototypes.
* One must ensure that [prototypeHolder] is a parent of this group.
*/ */
public fun SolidGroup.newRef( public fun SolidGroup.newRef(
name: String?, name: String?,
obj: Solid, obj: Solid,
prototypeHolder: PrototypeHolder = this, prototypeHolder: SolidGroup = this,
templateName: Name = Name.parse(name ?: obj.toString()), prototypeName: Name = Name.parse(name ?: obj.toString()),
): SolidReferenceGroup { ): SolidReference {
val existing = getPrototype(templateName) val existing = prototypeHolder.getPrototype(prototypeName)
if (existing == null) { if (existing == null) {
prototypeHolder.prototypes { prototypeHolder.prototypes {
set(templateName, obj) set(prototypeName, obj)
} }
} else if (existing != obj) { } else if (existing != obj) {
error("Can't add different prototype on top of existing one") error("Can't add different prototype on top of existing one")
} }
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>, 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<NameToken, VisionGroupItem<Solid>>
// 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>, 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<NameToken, VisionGroupItem<Solid>>
// 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"
// }
//}

View File

@ -29,7 +29,7 @@ public class Solids(meta: Meta) : VisionPlugin(meta) {
private fun PolymorphicModuleBuilder<Solid>.solids() { private fun PolymorphicModuleBuilder<Solid>.solids() {
subclass(SolidGroup.serializer()) subclass(SolidGroup.serializer())
subclass(SolidReferenceGroup.serializer()) subclass(SolidReference.serializer())
subclass(Composite.serializer()) subclass(Composite.serializer())
subclass(Box.serializer()) subclass(Box.serializer())
subclass(GenericHexagon.serializer()) subclass(GenericHexagon.serializer())
@ -47,8 +47,7 @@ public class Solids(meta: Meta) : VisionPlugin(meta) {
public val serializersModuleForSolids: SerializersModule = SerializersModule { public val serializersModuleForSolids: SerializersModule = SerializersModule {
polymorphic(Vision::class) { polymorphic(Vision::class) {
subclass(VisionBase.serializer()) subclass(VisionGroup.serializer())
subclass(VisionGroupBase.serializer())
solids() solids()
} }

View File

@ -21,7 +21,7 @@ public class Sphere(
) : SolidBase(), GeometrySolid, VisionPropertyContainer<Sphere> { ) : SolidBase(), GeometrySolid, VisionPropertyContainer<Sphere> {
override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) { override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) {
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 // This transformation matches three.js sphere implementation
val y = r * cos(theta) val y = r * cos(theta)
val z = r * sin(theta) * sin(phi) val z = r * sin(theta) * sin(phi)
@ -39,10 +39,10 @@ public class Sphere(
for (j in 0 until segments) { // phi iteration for (j in 0 until segments) { // phi iteration
val phi1 = phiStart + j * phiStep val phi1 = phiStart + j * phiStep
val phi2 = phi1 + phiStep val phi2 = phi1 + phiStep
val point1 = point3DfromSphCoord(radius, theta1, phi1) val point1 = point3dFromSphCoord(radius, theta1, phi1)
val point2 = point3DfromSphCoord(radius, theta1, phi2) val point2 = point3dFromSphCoord(radius, theta1, phi2)
val point3 = point3DfromSphCoord(radius, theta2, phi2) val point3 = point3dFromSphCoord(radius, theta2, phi2)
val point4 = point3DfromSphCoord(radius, theta2, phi1) val point4 = point3dFromSphCoord(radius, theta2, phi1)
geometryBuilder.apply { geometryBuilder.apply {
// 1-2-3-4 gives the same face but with opposite orientation // 1-2-3-4 gives the same face but with opposite orientation
face4(point1, point4, point3, point2) face4(point1, point4, point3, point2)

View File

@ -27,7 +27,7 @@ public class SphereLayer(
require(outerRadius > 0) { "Outer radius must be positive" } require(outerRadius > 0) { "Outer radius must be positive" }
require(innerRadius >= 0) { "inner radius must be non-negative" } 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 // This transformation matches three.js sphere implementation
val y = r * cos(theta) val y = r * cos(theta)
val z = r * sin(theta) * sin(phi) val z = r * sin(theta) * sin(phi)
@ -46,17 +46,17 @@ public class SphereLayer(
val phi1 = phiStart + j * phiStep val phi1 = phiStart + j * phiStep
val phi2 = phi1 + phiStep val phi2 = phi1 + phiStep
//outer points //outer points
val outerPoint1 = point3DfromSphCoord(outerRadius, theta1, phi1) val outerPoint1 = point3dFromSphCoord(outerRadius, theta1, phi1)
val outerPoint2 = point3DfromSphCoord(outerRadius, theta1, phi2) val outerPoint2 = point3dFromSphCoord(outerRadius, theta1, phi2)
val outerPoint3 = point3DfromSphCoord(outerRadius, theta2, phi2) val outerPoint3 = point3dFromSphCoord(outerRadius, theta2, phi2)
val outerPoint4 = point3DfromSphCoord(outerRadius, theta2, phi1) val outerPoint4 = point3dFromSphCoord(outerRadius, theta2, phi1)
// 1-2-3-4 gives the same face but with opposite orientation // 1-2-3-4 gives the same face but with opposite orientation
face4(outerPoint1, outerPoint4, outerPoint3, outerPoint2) face4(outerPoint1, outerPoint4, outerPoint3, outerPoint2)
if (innerRadius > 0) { if (innerRadius > 0) {
val innerPoint1 = point3DfromSphCoord(innerRadius, theta1, phi1) val innerPoint1 = point3dFromSphCoord(innerRadius, theta1, phi1)
val innerPoint2 = point3DfromSphCoord(innerRadius, theta1, phi2) val innerPoint2 = point3dFromSphCoord(innerRadius, theta1, phi2)
val innerPoint3 = point3DfromSphCoord(innerRadius, theta2, phi2) val innerPoint3 = point3dFromSphCoord(innerRadius, theta2, phi2)
val innerPoint4 = point3DfromSphCoord(innerRadius, theta2, phi1) val innerPoint4 = point3dFromSphCoord(innerRadius, theta2, phi1)
face4(innerPoint1, innerPoint2, innerPoint3, innerPoint4) face4(innerPoint1, innerPoint2, innerPoint3, innerPoint4)
//the cup //the cup
if (i == segments - 1 && theta != PI.toFloat() && innerRadius != outerRadius) { if (i == segments - 1 && theta != PI.toFloat() && innerRadius != outerRadius) {

View File

@ -112,9 +112,9 @@ public fun Point3D.toMeta(): Meta = Meta {
internal fun Meta.toVector(default: Float = 0f) = Point3D( internal fun Meta.toVector(default: Float = 0f) = Point3D(
this[Solid.X_KEY].float ?: default, this[X_KEY].float ?: default,
this[Solid.Y_KEY].float ?: default, this[Y_KEY].float ?: default,
this[Solid.Z_KEY].float ?: default this[Z_KEY].float ?: default
) )
//internal fun Solid.updatePosition(meta: Meta?) { //internal fun Solid.updatePosition(meta: Meta?) {

View File

@ -5,6 +5,7 @@ import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.meta.descriptors.scheme import space.kscience.dataforge.meta.descriptors.scheme
import space.kscience.dataforge.meta.descriptors.value import space.kscience.dataforge.meta.descriptors.value
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.values.set
import space.kscience.visionforge.hide import space.kscience.visionforge.hide
import space.kscience.visionforge.widgetType import space.kscience.visionforge.widgetType

View File

@ -1,31 +1,26 @@
package space.kscience.visionforge.solid.transform 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.misc.DFExperimental
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.asName
import space.kscience.visionforge.* import space.kscience.visionforge.getProperty
import space.kscience.visionforge.solid.* import space.kscience.visionforge.solid.*
private operator fun Number.plus(other: Number) = toFloat() + other.toFloat() private operator fun Number.plus(other: Number) = toFloat() + other.toFloat()
private operator fun Number.times(other: Number) = toFloat() * other.toFloat() private operator fun Number.times(other: Number) = toFloat() * other.toFloat()
@DFExperimental @DFExperimental
internal fun Vision.updateFrom(other: Vision): Vision { internal fun Solid.updateFrom(other: Solid): Solid {
if (this is Solid && other is Solid) { x += other.x
x += other.x y += other.y
y += other.y z += other.y
z += other.y rotationX += other.rotationX
rotationX += other.rotationX rotationY += other.rotationY
rotationY += other.rotationY rotationZ += other.rotationZ
rotationZ += other.rotationZ scaleX *= other.scaleX
scaleX *= other.scaleX scaleY *= other.scaleY
scaleY *= other.scaleY scaleZ *= other.scaleZ
scaleZ *= other.scaleZ setProperty(Name.EMPTY, other.getProperty(Name.EMPTY))
configure{
update(other.meta)
}
}
return this return this
} }
@ -34,17 +29,17 @@ internal fun Vision.updateFrom(other: Vision): Vision {
internal object RemoveSingleChild : VisualTreeTransform<SolidGroup>() { internal object RemoveSingleChild : VisualTreeTransform<SolidGroup>() {
override fun SolidGroup.transformInPlace() { override fun SolidGroup.transformInPlace() {
fun MutableVisionGroup.replaceChildren() { fun SolidGroup.replaceChildren() {
children.forEach { (childName, parent) -> items.forEach { (childName, parent) ->
if (parent is SolidReferenceGroup) return@forEach //ignore refs if (parent is SolidReference) return@forEach //ignore refs
if (parent is MutableVisionGroup) { if (parent is SolidGroup) {
parent.replaceChildren() parent.replaceChildren()
} }
if (parent is VisionGroup && parent.children.size == 1) { if (parent is SolidGroup && parent.items.size == 1) {
val child = parent.children.values.first() val child: Solid = parent.items.values.first()
val newParent = child.updateFrom(parent) val newParent = child.updateFrom(parent)
newParent.parent = null newParent.parent = null
set(childName.asName(), newParent) children[childName.asName()] = newParent
} }
} }
} }

View File

@ -2,41 +2,47 @@ package space.kscience.visionforge.solid.transform
import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.names.Name 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.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 @DFExperimental
internal object UnRef : VisualTreeTransform<SolidGroup>() { internal object UnRef : VisualTreeTransform<SolidGroup>() {
private fun VisionGroup.countRefs(): Map<Name, Int> { private fun SolidGroup.countRefs(): Map<Name, Int> {
return children.values.fold(HashMap()) { reducer, obj -> return items.values.fold(HashMap()) { reducer, vision ->
if (obj is VisionGroup) { if (vision is SolidGroup) {
val counter = obj.countRefs() val counter = vision.countRefs()
counter.forEach { (key, value) -> counter.forEach { (key, value) ->
reducer[key] = (reducer[key] ?: 0) + value reducer[key] = (reducer[key] ?: 0) + value
} }
} else if (obj is SolidReferenceGroup) { } else if (vision is SolidReference) {
reducer[obj.refName] = (reducer[obj.refName] ?: 0) + 1 reducer[vision.prototypeName] = (reducer[vision.prototypeName] ?: 0) + 1
} }
return reducer return reducer
} }
} }
private fun MutableVisionGroup.unref(name: Name) { private fun SolidGroup.unref(name: Name) {
(this as? SolidGroup)?.prototypes{ (this as? SolidGroup)?.prototypes{
set(name, null) set(name, null)
} }
children.filter { (it.value as? SolidReferenceGroup)?.refName == name }.forEach { (key, value) -> items.filter { (it.value as? SolidReference)?.prototypeName == name }.forEach { (key, value) ->
val reference = value as SolidReferenceGroup val reference = value as SolidReference
val newChild = reference.prototype.updateFrom(reference) val newChild = reference.prototype.updateFrom(reference)
newChild.parent = null newChild.parent = null
set(key.asName(), newChild) // replace proxy with merged object children[key] = newChild // replace proxy with merged object
} }
children.values.filterIsInstance<MutableVisionGroup>().forEach { it.unref(name) } items.values.filterIsInstance<SolidGroup>().forEach { it.unref(name) }
} }
override fun SolidGroup.transformInPlace() { override fun SolidGroup.transformInPlace() {

View File

@ -18,7 +18,7 @@ class CompositeTest {
detail = 32 detail = 32
} }
material { material {
color("pink") color.set("pink")
} }
} }
} }

View File

@ -1,7 +1,6 @@
package space.kscience.visionforge.solid package space.kscience.visionforge.solid
import space.kscience.dataforge.meta.getIndexed import space.kscience.dataforge.meta.getIndexed
import space.kscience.dataforge.meta.node
import space.kscience.dataforge.meta.toMeta import space.kscience.dataforge.meta.toMeta
import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.misc.DFExperimental
import kotlin.test.Test import kotlin.test.Test
@ -12,7 +11,7 @@ class ConvexTest {
@Suppress("UNUSED_VARIABLE") @Suppress("UNUSED_VARIABLE")
@Test @Test
fun testConvexBuilder() { fun testConvexBuilder() {
val group = SolidGroup().apply { val group = SolidGroup{
convex { convex {
point(50, 50, -50) point(50, 50, -50)
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 json = Solids.jsonForSolids.encodeToJsonElement(Convex.serializer(), convex)
val meta = json.toMeta() val meta = json.toMeta()

View File

@ -9,7 +9,7 @@ import kotlin.test.assertEquals
class GroupTest { class GroupTest {
@Test @Test
fun testGroupWithComposite() { fun testGroupWithComposite() {
val group = SolidGroup().apply { val group = SolidGroup{
union("union") { union("union") {
box(100, 100, 100) { box(100, 100, 100) {
z = 100 z = 100
@ -18,7 +18,7 @@ class GroupTest {
} }
box(100, 100, 100) box(100, 100, 100)
material { material {
color(Colors.lightgreen) color.set(Colors.lightgreen)
opacity = 0.3f opacity = 0.3f
} }
} }
@ -30,7 +30,7 @@ class GroupTest {
} }
box(100, 100, 100) box(100, 100, 100)
y = 300 y = 300
color(Colors.red) color.set(Colors.red)
} }
subtract("subtract") { subtract("subtract") {
box(100, 100, 100) { box(100, 100, 100) {
@ -40,12 +40,12 @@ class GroupTest {
} }
box(100, 100, 100) box(100, 100, 100)
y = -300 y = -300
color(Colors.blue) color.set(Colors.blue)
} }
} }
assertEquals(3, group.children.count()) assertEquals(3, group.items.count())
assertEquals(300.0, (group["intersect"] as Solid).y.toDouble()) assertEquals(300.0, (group.children["intersect"] as Solid).y.toDouble())
assertEquals(-300.0, (group["subtract"] as Solid).y.toDouble()) assertEquals(-300.0, (group.children["subtract"] as Solid).y.toDouble())
} }
} }

View File

@ -15,7 +15,7 @@ class PropertyTest {
val box = Box(10.0f, 10.0f,10.0f) val box = Box(10.0f, 10.0f,10.0f)
box.material { box.material {
//meta["color"] = "pink" //meta["color"] = "pink"
color("pink") color.set("pink")
} }
assertEquals("pink", box.meta["material.color"]?.string) assertEquals("pink", box.meta["material.color"]?.string)
assertEquals("pink", box.color.string) assertEquals("pink", box.color.string)
@ -33,7 +33,7 @@ class PropertyTest {
} }
box.material { box.material {
color("pink") color.set("pink")
} }
assertEquals("pink", c) assertEquals("pink", c)
@ -43,7 +43,7 @@ class PropertyTest {
fun testInheritedProperty() { fun testInheritedProperty() {
var box: Box? = null var box: Box? = null
val group = SolidGroup().apply { val group = SolidGroup().apply {
setPropertyNode("test", 22) setPropertyValue("test", 22)
group { group {
box = box(100, 100, 100) box = box(100, 100, 100)
} }
@ -54,14 +54,14 @@ class PropertyTest {
@Test @Test
fun testStyleProperty() { fun testStyleProperty() {
var box: Box? = null var box: Box? = null
val group = SolidGroup().apply { val group = SolidGroup{
styleSheet { styleSheet {
set("testStyle") { update("testStyle") {
"test" put 22 "test" put 22
} }
} }
group { group {
box = box(100, 100, 100).apply { box = box(100, 100, 100) {
useStyle("testStyle") useStyle("testStyle")
} }
} }
@ -74,7 +74,7 @@ class PropertyTest {
var box: Box? = null var box: Box? = null
val group = SolidGroup().apply { val group = SolidGroup().apply {
styleSheet { styleSheet {
set("testStyle") { update("testStyle") {
SolidMaterial.MATERIAL_COLOR_KEY put "#555555" SolidMaterial.MATERIAL_COLOR_KEY put "#555555"
} }
} }
@ -89,10 +89,10 @@ class PropertyTest {
@Test @Test
fun testReferenceStyleProperty() { fun testReferenceStyleProperty() {
var box: SolidReferenceGroup? = null var box: SolidReference? = null
val group = SolidGroup{ val group = SolidGroup{
styleSheet { styleSheet {
set("testStyle") { update("testStyle") {
SolidMaterial.MATERIAL_COLOR_KEY put "#555555" SolidMaterial.MATERIAL_COLOR_KEY put "#555555"
} }
} }
@ -105,6 +105,6 @@ class PropertyTest {
box = ref("box".asName()) box = ref("box".asName())
} }
} }
assertEquals("#555555", box?.color.string) assertEquals("#555555", box!!.color.string)
} }
} }

View File

@ -2,7 +2,6 @@ package space.kscience.visionforge.solid
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.visionforge.Colors import space.kscience.visionforge.Colors
import space.kscience.visionforge.MutableVisionGroup
import space.kscience.visionforge.get import space.kscience.visionforge.get
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -14,10 +13,10 @@ import kotlin.test.assertEquals
fun SolidGroup.refGroup( fun SolidGroup.refGroup(
name: String, name: String,
templateName: Name = Name.parse(name), templateName: Name = Name.parse(name),
block: MutableVisionGroup.() -> Unit block: SolidGroup.() -> Unit
): SolidReferenceGroup { ): SolidReference {
val group = SolidGroup().apply(block) val group = SolidGroup().apply(block)
return newRef(name, group, templateName = templateName) return newRef(name, group, prototypeName = templateName)
} }
@ -25,7 +24,7 @@ class SerializationTest {
@Test @Test
fun testCubeSerialization() { fun testCubeSerialization() {
val cube = Box(100f, 100f, 100f).apply { val cube = Box(100f, 100f, 100f).apply {
color(222) color.set(222)
x = 100 x = 100
z = -100 z = -100
} }
@ -38,7 +37,7 @@ class SerializationTest {
@Test @Test
fun testProxySerialization() { fun testProxySerialization() {
val cube = Box(100f, 100f, 100f).apply { val cube = Box(100f, 100f, 100f).apply {
color(222) color.set(222)
x = 100 x = 100
z = -100 z = -100
} }
@ -53,21 +52,21 @@ class SerializationTest {
val string = Solids.encodeToString(group) val string = Solids.encodeToString(group)
println(string) println(string)
val reconstructed = Solids.decodeFromString(string) as SolidGroup val reconstructed = Solids.decodeFromString(string) as SolidGroup
assertEquals(group["cube"]?.meta, reconstructed["cube"]?.meta) assertEquals(group.children["cube"]?.meta, reconstructed.children["cube"]?.meta)
} }
@Test @Test
fun lightSerialization(){ fun lightSerialization(){
val group = SolidGroup { val group = SolidGroup {
ambientLight { ambientLight {
color(Colors.white) color.set(Colors.white)
intensity = 100.0 intensity = 100.0
} }
} }
val serialized = Solids.encodeToString(group) val serialized = Solids.encodeToString(group)
val reconstructed = Solids.decodeFromString(serialized) as SolidGroup val reconstructed = Solids.decodeFromString(serialized) as SolidGroup
assertEquals(100.0, (reconstructed["@ambientLight"] as AmbientLightSource).intensity.toDouble()) assertEquals(100.0, (reconstructed.children["@ambientLight"] as AmbientLightSource).intensity.toDouble())
} }
} }

View File

@ -24,6 +24,9 @@ class SolidPluginTest {
val reconstructed = visionManager.decodeFromMeta(meta) as SolidGroup 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"]!!)
)
} }
} }

View File

@ -15,7 +15,7 @@ class SolidReferenceTest {
SolidMaterial.MATERIAL_COLOR_KEY put "red" SolidMaterial.MATERIAL_COLOR_KEY put "red"
} }
newRef("test", Box(100f,100f,100f).apply { newRef("test", Box(100f,100f,100f).apply {
color("blue") color.set("blue")
useStyle(theStyle) useStyle(theStyle)
}) })
} }
@ -23,13 +23,13 @@ class SolidReferenceTest {
@Test @Test
fun testReferenceProperty(){ fun testReferenceProperty(){
assertEquals("blue", (groupWithReference["test"] as Solid).color.string) assertEquals("blue", (groupWithReference.children["test"] as Solid).color.string)
} }
@Test @Test
fun testReferenceSerialization(){ fun testReferenceSerialization(){
val serialized = Solids.jsonForSolids.encodeToJsonElement(groupWithReference) val serialized = Solids.jsonForSolids.encodeToJsonElement(groupWithReference)
val deserialized = Solids.jsonForSolids.decodeFromJsonElement(SolidGroup.serializer(), serialized) val deserialized = Solids.jsonForSolids.decodeFromJsonElement(SolidGroup.serializer(), serialized)
assertEquals("blue", (deserialized["test"] as Solid).color.string) assertEquals("blue", (deserialized.children["test"] as Solid).color.string)
} }
} }

View File

@ -21,24 +21,24 @@ class VisionUpdateTest {
box(200,200,200, name = "origin") box(200,200,200, name = "origin")
} }
val dif = VisionChange{ val dif = VisionChange{
group("top") { group ("top") {
color(123) color.set(123)
box(100,100,100) box(100,100,100)
} }
propertyChanged("top".asName(), SolidMaterial.MATERIAL_COLOR_KEY, Meta("red".asValue())) propertyChanged("top".asName(), SolidMaterial.MATERIAL_COLOR_KEY, Meta("red".asValue()))
propertyChanged("origin".asName(), SolidMaterial.MATERIAL_COLOR_KEY, Meta("red".asValue())) propertyChanged("origin".asName(), SolidMaterial.MATERIAL_COLOR_KEY, Meta("red".asValue()))
} }
targetVision.update(dif) targetVision.update(dif)
assertTrue { targetVision["top"] is SolidGroup } assertTrue { targetVision.children["top"] is SolidGroup }
assertEquals("red", (targetVision["origin"] as Solid).color.string) // Should work assertEquals("red", (targetVision.children["origin"] as Solid).color.string) // Should work
assertEquals("#00007b", (targetVision["top"] as Solid).color.string) // new item always takes precedence assertEquals("#00007b", (targetVision.children["top"] as Solid).color.string) // new item always takes precedence
} }
@Test @Test
fun testVisionChangeSerialization(){ fun testVisionChangeSerialization(){
val change = VisionChange{ val change = VisionChange{
group("top") { group("top") {
color(123) color.set(123)
box(100,100,100) box(100,100,100)
} }
propertyChanged("top".asName(), SolidMaterial.MATERIAL_COLOR_KEY, Meta("red".asValue())) propertyChanged("top".asName(), SolidMaterial.MATERIAL_COLOR_KEY, Meta("red".asValue()))

View File

@ -12,8 +12,9 @@ import space.kscience.dataforge.values.Null
import space.kscience.dataforge.values.Value import space.kscience.dataforge.values.Value
import space.kscience.dataforge.values.asValue import space.kscience.dataforge.values.asValue
import space.kscience.tables.* import space.kscience.tables.*
import space.kscience.visionforge.VisionBase import space.kscience.visionforge.VisionGroup
import space.kscience.visionforge.html.VisionOutput import space.kscience.visionforge.html.VisionOutput
import space.kscience.visionforge.properties
import kotlin.jvm.JvmName import kotlin.jvm.JvmName
import kotlin.reflect.typeOf import kotlin.reflect.typeOf
@ -41,12 +42,13 @@ public val ColumnHeader<Value>.properties: ValueColumnScheme get() = ValueColumn
@SerialName("vision.table") @SerialName("vision.table")
public class VisionOfTable( public class VisionOfTable(
override val headers: List<@Serializable(ColumnHeaderSerializer::class) ColumnHeader<Value>>, override val headers: List<@Serializable(ColumnHeaderSerializer::class) ColumnHeader<Value>>,
) : VisionBase(), Rows<Value> { ) : VisionGroup(), Rows<Value> {
public var data: List<Meta> public var data: List<Meta>
get() = meta.getIndexed("rows").entries.sortedBy { it.key?.toInt() }.map { it.value } get() = meta.getIndexed("rows").entries.sortedBy { it.key?.toInt() }.map { it.value }
set(value) { set(value) {
meta["rows"] = value //TODO Make it better
properties()["rows"] = value
} }
public val rows: List<MetaRow> get() = data.map(::MetaRow) public val rows: List<MetaRow> get() = data.map(::MetaRow)

View File

@ -4,16 +4,15 @@ import info.laht.threekt.core.BufferGeometry
import info.laht.threekt.geometries.EdgesGeometry import info.laht.threekt.geometries.EdgesGeometry
import info.laht.threekt.objects.LineSegments import info.laht.threekt.objects.LineSegments
import info.laht.threekt.objects.Mesh 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.Name
import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.asName
import space.kscience.dataforge.names.plus import space.kscience.dataforge.names.plus
import space.kscience.dataforge.names.startsWith import space.kscience.dataforge.names.startsWith
import space.kscience.dataforge.values.boolean
import space.kscience.visionforge.VisionBuilder import space.kscience.visionforge.VisionBuilder
import space.kscience.visionforge.computePropertyNode import space.kscience.visionforge.getProperty
import space.kscience.visionforge.onPropertyChange 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.Solid
import space.kscience.visionforge.solid.SolidMaterial import space.kscience.visionforge.solid.SolidMaterial
import space.kscience.visionforge.solid.layer import space.kscience.visionforge.solid.layer
@ -32,7 +31,7 @@ public abstract class MeshThreeFactory<in T : Solid>(
*/ */
public abstract fun buildGeometry(obj: T): BufferGeometry 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 geometry = buildGeometry(obj)
//val meshMeta: Meta = obj.properties[Material3D.MATERIAL_KEY]?.node ?: Meta.empty //val meshMeta: Meta = obj.properties[Material3D.MATERIAL_KEY]?.node ?: Meta.empty
@ -78,8 +77,8 @@ public abstract class MeshThreeFactory<in T : Solid>(
@VisionBuilder @VisionBuilder
public fun Solid.edges(enabled: Boolean = true, block: SolidMaterial.() -> Unit = {}) { public fun Solid.edges(enabled: Boolean = true, block: SolidMaterial.() -> Unit = {}) {
setProperty(EDGES_ENABLED_KEY, enabled) setPropertyValue(EDGES_ENABLED_KEY, enabled)
meta.getOrCreate(EDGES_MATERIAL_KEY).updateWith(SolidMaterial, block) SolidMaterial.write(getProperty(EDGES_MATERIAL_KEY)).apply(block)
} }
internal fun Mesh.applyProperties(obj: Solid): Mesh = apply { 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) { public fun Mesh.applyEdges(obj: Solid) {
val edges = children.find { it.name == "@edges" } as? LineSegments val edges = children.find { it.name == "@edges" } as? LineSegments
//inherited edges definition, enabled by default //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 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) { if (edges == null) {
add( add(
LineSegments( LineSegments(

View File

@ -8,7 +8,7 @@ import kotlin.reflect.KClass
public object ThreeAmbientLightFactory : ThreeFactory<AmbientLightSource> { public object ThreeAmbientLightFactory : ThreeFactory<AmbientLightSource> {
override val type: KClass<in AmbientLightSource> get() = AmbientLightSource::class override val type: KClass<in AmbientLightSource> get() = AmbientLightSource::class
override fun invoke(three: ThreePlugin, obj: AmbientLightSource): AmbientLight { override fun build(three: ThreePlugin, obj: AmbientLightSource): AmbientLight {
val res = AmbientLight().apply { val res = AmbientLight().apply {
color = obj.color.threeColor() ?: Color(0x404040) color = obj.color.threeColor() ?: Color(0x404040)
intensity = obj.intensity.toDouble() intensity = obj.intensity.toDouble()

View File

@ -11,6 +11,7 @@ import org.w3c.dom.CanvasRenderingContext2D
import org.w3c.dom.CanvasTextBaseline import org.w3c.dom.CanvasTextBaseline
import org.w3c.dom.HTMLCanvasElement import org.w3c.dom.HTMLCanvasElement
import org.w3c.dom.MIDDLE import org.w3c.dom.MIDDLE
import space.kscience.visionforge.getProperty
import space.kscience.visionforge.solid.SolidLabel import space.kscience.visionforge.solid.SolidLabel
import space.kscience.visionforge.solid.SolidMaterial import space.kscience.visionforge.solid.SolidMaterial
import space.kscience.visionforge.solid.three.ThreeCanvas.Companion.DO_NOT_HIGHLIGHT_TAG import space.kscience.visionforge.solid.three.ThreeCanvas.Companion.DO_NOT_HIGHLIGHT_TAG
@ -22,11 +23,11 @@ import kotlin.reflect.KClass
public object ThreeCanvasLabelFactory : ThreeFactory<SolidLabel> { public object ThreeCanvasLabelFactory : ThreeFactory<SolidLabel> {
override val type: KClass<in SolidLabel> get() = SolidLabel::class override val type: KClass<in SolidLabel> 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 canvas = document.createElement("canvas") as HTMLCanvasElement
val context = canvas.getContext("2d") as CanvasRenderingContext2D val context = canvas.getContext("2d") as CanvasRenderingContext2D
context.font = "Bold ${obj.fontSize}pt ${obj.fontFamily}" 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 context.textBaseline = CanvasTextBaseline.MIDDLE
val metrics = context.measureText(obj.text) val metrics = context.measureText(obj.text)
//canvas.width = metrics.width.toInt() //canvas.width = metrics.width.toInt()

View File

@ -37,7 +37,7 @@ public class ThreeCompositeFactory(public val three: ThreePlugin) : ThreeFactory
override val type: KClass<in Composite> get() = Composite::class override val type: KClass<in Composite> 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 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") val second = three.buildObject3D(obj.second).takeIfMesh() ?: error("Second part of composite is not a mesh")
return when (obj.compositeType) { return when (obj.compositeType) {

View File

@ -22,7 +22,7 @@ public interface ThreeFactory<in T : Vision> {
public val type: KClass<in T> public val type: KClass<in T>
public operator fun invoke(three: ThreePlugin, obj: T): Object3D public fun build(three: ThreePlugin, obj: T): Object3D
public companion object { public companion object {
public const val TYPE: String = "threeFactory" public const val TYPE: String = "threeFactory"

View File

@ -17,7 +17,7 @@ import kotlin.reflect.KClass
public object ThreeLabelFactory : ThreeFactory<SolidLabel> { public object ThreeLabelFactory : ThreeFactory<SolidLabel> {
override val type: KClass<in SolidLabel> get() = SolidLabel::class override val type: KClass<in SolidLabel> 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 { val textGeo = TextBufferGeometry(obj.text, jso {
font = obj.fontFamily font = obj.fontFamily
size = 20 size = 20

View File

@ -4,7 +4,7 @@ import info.laht.threekt.core.BufferGeometry
import info.laht.threekt.core.Object3D import info.laht.threekt.core.Object3D
import info.laht.threekt.math.Color import info.laht.threekt.math.Color
import info.laht.threekt.objects.LineSegments 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.onPropertyChange
import space.kscience.visionforge.solid.PolyLine import space.kscience.visionforge.solid.PolyLine
import space.kscience.visionforge.solid.SolidMaterial import space.kscience.visionforge.solid.SolidMaterial
@ -17,7 +17,7 @@ import kotlin.reflect.KClass
public object ThreeLineFactory : ThreeFactory<PolyLine> { public object ThreeLineFactory : ThreeFactory<PolyLine> {
override val type: KClass<PolyLine> get() = PolyLine::class override val type: KClass<PolyLine> get() = PolyLine::class
override fun invoke(three: ThreePlugin, obj: PolyLine): Object3D { override fun build(three: ThreePlugin, obj: PolyLine): Object3D {
val geometry = BufferGeometry().apply { val geometry = BufferGeometry().apply {
setFromPoints(Array((obj.points.size - 1) * 2) { setFromPoints(Array((obj.points.size - 1) * 2) {
obj.points[ceil(it / 2.0).toInt()].toVector() obj.points[ceil(it / 2.0).toInt()].toVector()
@ -25,7 +25,7 @@ public object ThreeLineFactory : ThreeFactory<PolyLine> {
} }
val material = ThreeMaterials.getLineMaterial( val material = ThreeMaterials.getLineMaterial(
obj.computePropertyNode(SolidMaterial.MATERIAL_KEY), obj.getProperty(SolidMaterial.MATERIAL_KEY),
false false
) )

View File

@ -171,7 +171,7 @@ public fun Mesh.updateMaterialProperty(vision: Vision, propertyName: Name) {
?: ThreeMaterials.BLACK_COLOR ?: ThreeMaterials.BLACK_COLOR
} }
SolidMaterial.MATERIAL_OPACITY_KEY -> { SolidMaterial.MATERIAL_OPACITY_KEY -> {
val opacity = vision.getPropertyValue( val opacity = vision.getProperty(
SolidMaterial.MATERIAL_OPACITY_KEY, SolidMaterial.MATERIAL_OPACITY_KEY,
inherit = true, inherit = true,
)?.double ?: 1.0 )?.double ?: 1.0
@ -179,7 +179,7 @@ public fun Mesh.updateMaterialProperty(vision: Vision, propertyName: Name) {
material.transparent = opacity < 1.0 material.transparent = opacity < 1.0
} }
SolidMaterial.MATERIAL_WIREFRAME_KEY -> { SolidMaterial.MATERIAL_WIREFRAME_KEY -> {
material.asDynamic().wireframe = vision.getPropertyValue( material.asDynamic().wireframe = vision.getProperty(
SolidMaterial.MATERIAL_WIREFRAME_KEY, SolidMaterial.MATERIAL_WIREFRAME_KEY,
inherit = true, inherit = true,
)?.boolean ?: false )?.boolean ?: false

View File

@ -12,7 +12,6 @@ import space.kscience.visionforge.Vision
import space.kscience.visionforge.onPropertyChange import space.kscience.visionforge.onPropertyChange
import space.kscience.visionforge.solid.* import space.kscience.visionforge.solid.*
import space.kscience.visionforge.solid.specifications.Canvas3DOptions import space.kscience.visionforge.solid.specifications.Canvas3DOptions
import space.kscience.visionforge.solid.three.set
import space.kscience.visionforge.visible import space.kscience.visionforge.visible
import kotlin.collections.set import kotlin.collections.set
import kotlin.reflect.KClass import kotlin.reflect.KClass
@ -49,10 +48,10 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
public fun buildObject3D(obj: Solid): Object3D = when (obj) { public fun buildObject3D(obj: Solid): Object3D = when (obj) {
is ThreeJsVision -> obj.render(this) is ThreeJsVision -> obj.render(this)
is SolidReferenceGroup -> ThreeReferenceFactory(this, obj) is SolidReferenceGroup -> ThreeReferenceFactory.build(this, obj)
is SolidGroup -> { is SolidGroup -> {
val group = ThreeGroup() val group = ThreeGroup()
obj.children.forEach { (token, child) -> obj.items.forEach { (token, child) ->
if (child is Solid && token != SolidGroup.PROTOTYPES_TOKEN && child.ignore != true) { if (child is Solid && token != SolidGroup.PROTOTYPES_TOKEN && child.ignore != true) {
try { try {
val object3D = buildObject3D(child) 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 -> { else -> {
//find specialized factory for this type if it is present //find specialized factory for this type if it is present
val factory: ThreeFactory<Solid>? = findObjectFactory(obj::class) val factory: ThreeFactory<Solid>? = findObjectFactory(obj::class)
when { when {
factory != null -> factory(this, obj) factory != null -> factory.build(this, obj)
obj is GeometrySolid -> ThreeShapeFactory(this, obj) obj is GeometrySolid -> ThreeShapeFactory.build(this, obj)
else -> error("Renderer for ${obj::class} not found") else -> error("Renderer for ${obj::class} not found")
} }
} }

View File

@ -11,17 +11,19 @@ import kotlin.reflect.KClass
public object ThreePointLightFactory : ThreeFactory<PointLightSource> { public object ThreePointLightFactory : ThreeFactory<PointLightSource> {
override val type: KClass<in PointLightSource> get() = PointLightSource::class override val type: KClass<in PointLightSource> 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 { val res = PointLight().apply {
matrixAutoUpdate = false matrixAutoUpdate = false
color = obj.color.threeColor() ?: Color(0x404040) color = obj.color.threeColor() ?: DEFAULT_COLOR
intensity = obj.intensity.toDouble() intensity = obj.intensity.toDouble()
updatePosition(obj) updatePosition(obj)
} }
obj.onPropertyChange { name -> obj.onPropertyChange { name ->
when (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() LightSource::intensity.name.asName() -> res.intensity = obj.intensity.toDouble()
else -> res.updateProperty(obj, name) else -> res.updateProperty(obj, name)
} }
@ -29,4 +31,5 @@ public object ThreePointLightFactory : ThreeFactory<PointLightSource> {
return res return res
} }
} }

View File

@ -30,7 +30,7 @@ public object ThreeReferenceFactory : ThreeFactory<SolidReferenceGroup> {
} }
} }
override fun invoke(three: ThreePlugin, obj: SolidReferenceGroup): Object3D { override fun build(three: ThreePlugin, obj: SolidReferenceGroup): Object3D {
val template = obj.prototype val template = obj.prototype
val cachedObject = cache.getOrPut(template) { val cachedObject = cache.getOrPut(template) {
three.buildObject3D(template) three.buildObject3D(template)
@ -50,7 +50,7 @@ public object ThreeReferenceFactory : ThreeFactory<SolidReferenceGroup> {
if (name.firstOrNull()?.body == REFERENCE_CHILD_PROPERTY_PREFIX) { if (name.firstOrNull()?.body == REFERENCE_CHILD_PROPERTY_PREFIX) {
val childName = name.firstOrNull()?.index?.let(Name::parse) ?: error("Wrong syntax for reference child property: '$name'") val childName = name.firstOrNull()?.index?.let(Name::parse) ?: error("Wrong syntax for reference child property: '$name'")
val propertyName = name.cutFirst() val propertyName = name.cutFirst()
val referenceChild = obj[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") val child = object3D.findChild(childName) ?: error("Object child with name '$childName' not found")
child.updateProperty(referenceChild, propertyName) child.updateProperty(referenceChild, propertyName)
} else { } else {