forked from kscience/visionforge
Compare commits
25 Commits
master
...
refactor/c
Author | SHA1 | Date | |
---|---|---|---|
67afa4e45b | |||
cb25dca34c | |||
98bb935de5 | |||
e2f281debe | |||
ac651c4d50 | |||
846e87a44b | |||
34fbb23c60 | |||
eeec89f0e6 | |||
43362f51f5 | |||
c586a2ea14 | |||
ecf4a6a198 | |||
0ea1ee056a | |||
9221df785d | |||
47bde02488 | |||
c71042ae06 | |||
9b1ca8332b | |||
791d6d7a81 | |||
4b1149b99b | |||
86935ce52a | |||
ce02a18c09 | |||
212d729afb | |||
3198bad094 | |||
9648533ac8 | |||
7ee40679b9 | |||
f828f86e29 |
2
.gitignore
vendored
2
.gitignore
vendored
@ -7,3 +7,5 @@ build/
|
|||||||
data/
|
data/
|
||||||
|
|
||||||
!gradle-wrapper.jar
|
!gradle-wrapper.jar
|
||||||
|
|
||||||
|
/kotlin-js-store/yarn.lock
|
||||||
|
@ -2,8 +2,16 @@
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
### Added
|
### Added
|
||||||
|
- Context receivers flag
|
||||||
|
- MeshLine for thick lines
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
- Visions **must** be rooted in order to subscribe to updates.
|
||||||
|
- Visions use flows instead of direct subscriptions.
|
||||||
|
- Radical change of inner workings of vision children and properties.
|
||||||
|
- Three package changed to `three`.
|
||||||
|
- Naming of Canvas3D options.
|
||||||
|
- Lights are added to the scene instead of 3D options.
|
||||||
|
|
||||||
### Deprecated
|
### Deprecated
|
||||||
|
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id("ru.mipt.npm.gradle.project")
|
id("space.kscience.gradle.project")
|
||||||
id("org.jetbrains.kotlinx.kover") version "0.5.0-RC"
|
// id("org.jetbrains.kotlinx.kover") version "0.5.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
val dataforgeVersion by extra("0.5.2")
|
val dataforgeVersion by extra("0.6.0-dev-13")
|
||||||
val fxVersion by extra("11")
|
val fxVersion by extra("11")
|
||||||
|
|
||||||
allprojects{
|
allprojects{
|
||||||
group = "space.kscience"
|
group = "space.kscience"
|
||||||
version = "0.2.0"
|
version = "0.3.0-dev-2"
|
||||||
}
|
}
|
||||||
|
|
||||||
subprojects {
|
subprojects {
|
||||||
@ -19,6 +19,12 @@ subprojects {
|
|||||||
mavenCentral()
|
mavenCentral()
|
||||||
maven("https://maven.jzy3d.org/releases")
|
maven("https://maven.jzy3d.org/releases")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>{
|
||||||
|
kotlinOptions{
|
||||||
|
freeCompilerArgs = freeCompilerArgs + "-Xcontext-receivers"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ksciencePublish {
|
ksciencePublish {
|
||||||
@ -31,4 +37,9 @@ apiValidation {
|
|||||||
ignoredPackages.add("info.laht.threekt")
|
ignoredPackages.add("info.laht.threekt")
|
||||||
}
|
}
|
||||||
|
|
||||||
readme.readmeTemplate = file("docs/templates/README-TEMPLATE.md")
|
readme.readmeTemplate = file("docs/templates/README-TEMPLATE.md")
|
||||||
|
|
||||||
|
|
||||||
|
//rootProject.extensions.configure<org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension> {
|
||||||
|
// versions.webpackCli.version = "4.10.0"
|
||||||
|
//}
|
@ -1,5 +1,5 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id("ru.mipt.npm.gradle.mpp")
|
id("space.kscience.gradle.mpp")
|
||||||
}
|
}
|
||||||
|
|
||||||
kscience{
|
kscience{
|
||||||
|
@ -5,7 +5,6 @@ import space.kscience.dataforge.meta.*
|
|||||||
import space.kscience.dataforge.misc.Named
|
import space.kscience.dataforge.misc.Named
|
||||||
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.values.doubleArray
|
|
||||||
import kotlin.properties.ReadOnlyProperty
|
import kotlin.properties.ReadOnlyProperty
|
||||||
|
|
||||||
public fun MetaProvider.doubleArray(
|
public fun MetaProvider.doubleArray(
|
||||||
|
@ -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.doubleArray
|
||||||
|
import space.kscience.dataforge.meta.get
|
||||||
|
import space.kscience.dataforge.meta.int
|
||||||
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.visionforge.MutableVisionContainer
|
||||||
import space.kscience.visionforge.isEmpty
|
import space.kscience.visionforge.isEmpty
|
||||||
|
import space.kscience.visionforge.set
|
||||||
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 Tait–Bryan angles according to https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
|
// converting to XYZ to Tait–Bryan angles according to https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
|
||||||
@ -42,14 +46,17 @@ private fun Solid.useMatrix(matrix: DGeoMatrix?) {
|
|||||||
"TGeoIdentity" -> {
|
"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,17 +258,19 @@ 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)
|
||||||
fShape?.let { scaledShape ->
|
fShape?.let { scaledShape ->
|
||||||
group(name?.let { Name.parse(it) }) {
|
solidGroup(name?.let { Name.parse(it) }) {
|
||||||
scale = Point3D(fScale?.x ?: 1.0, fScale?.y ?: 1.0, fScale?.z ?: 1.0)
|
scale = Point3D(fScale?.x ?: 1.0, fScale?.y ?: 1.0, fScale?.z ?: 1.0)
|
||||||
addShape(scaledShape, context)
|
addShape(scaledShape, context)
|
||||||
apply(block)
|
apply(block)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
@ -276,7 +295,7 @@ private fun SolidGroup.addRootNode(obj: DGeoNode, context: RootToSolidContext) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun buildVolume(volume: DGeoVolume, context: RootToSolidContext): Solid? {
|
private fun buildVolume(volume: DGeoVolume, context: RootToSolidContext): Solid? {
|
||||||
val group = SolidGroup {
|
val group = SolidGroup().apply {
|
||||||
//set current layer
|
//set current layer
|
||||||
layer = context.currentLayer
|
layer = context.currentLayer
|
||||||
val nodes = volume.fNodes
|
val nodes = volume.fNodes
|
||||||
@ -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.properties.own == null) {
|
||||||
(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,33 +349,33 @@ 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]
|
properties[MATERIAL_COLOR_KEY] = RootColors[it]
|
||||||
}
|
}
|
||||||
block()
|
block()
|
||||||
}
|
}
|
||||||
set(combinedName?.let { Name.parse(it) }, group)
|
setChild(combinedName?.let { Name.parse(it) }, group)
|
||||||
} else {
|
} else {
|
||||||
val templateName = volumesName + volume.name
|
val templateName = volumesName + volume.name
|
||||||
val existing = getPrototype(templateName)
|
val existing = getPrototype(templateName)
|
||||||
if (existing == null) {
|
if (existing == null) {
|
||||||
context.prototypeHolder.prototypes {
|
context.prototypeHolder.prototypes {
|
||||||
val group = buildVolume(volume, context)
|
val group = buildVolume(volume, context)
|
||||||
set(templateName, group)
|
setChild(templateName, group)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ref(templateName, name).apply {
|
ref(templateName, name).apply {
|
||||||
volume.fFillColor?.let {
|
volume.fFillColor?.let {
|
||||||
meta[MATERIAL_COLOR_KEY] = RootColors[it]
|
properties[MATERIAL_COLOR_KEY] = RootColors[it]
|
||||||
}
|
}
|
||||||
block()
|
block()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun DGeoManager.toSolid(): SolidGroup = SolidGroup {
|
public fun MutableVisionContainer<Solid>.rootGeo(dGeoManager: DGeoManager): SolidGroup = solidGroup {
|
||||||
val context = RootToSolidContext(this)
|
val context = RootToSolidContext(this)
|
||||||
fNodes.forEach { node ->
|
dGeoManager.fNodes.forEach { node ->
|
||||||
addRootNode(node, context)
|
addRootNode(node, context)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -14,7 +14,7 @@ import kotlinx.serialization.modules.subclass
|
|||||||
|
|
||||||
private fun <T> jsonRootDeserializer(
|
private fun <T> jsonRootDeserializer(
|
||||||
tSerializer: KSerializer<T>,
|
tSerializer: KSerializer<T>,
|
||||||
builder: (JsonElement) -> T
|
builder: (JsonElement) -> T,
|
||||||
): DeserializationStrategy<T> = object :
|
): DeserializationStrategy<T> = object :
|
||||||
DeserializationStrategy<T> {
|
DeserializationStrategy<T> {
|
||||||
private val jsonElementSerializer = JsonElement.serializer()
|
private val jsonElementSerializer = JsonElement.serializer()
|
||||||
@ -46,6 +46,7 @@ private object RootDecoder {
|
|||||||
private val refCache: List<RefEntry>,
|
private val refCache: List<RefEntry>,
|
||||||
) : KSerializer<T> by tSerializer {
|
) : KSerializer<T> by tSerializer {
|
||||||
|
|
||||||
|
@OptIn(ExperimentalSerializationApi::class)
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
override fun deserialize(decoder: Decoder): T {
|
override fun deserialize(decoder: Decoder): T {
|
||||||
val input = decoder as JsonDecoder
|
val input = decoder as JsonDecoder
|
||||||
@ -90,9 +91,8 @@ private object RootDecoder {
|
|||||||
|
|
||||||
private fun <T> KSerializer<T>.unref(refCache: List<RefEntry>): KSerializer<T> = RootUnrefSerializer(this, refCache)
|
private fun <T> KSerializer<T>.unref(refCache: List<RefEntry>): KSerializer<T> = RootUnrefSerializer(this, refCache)
|
||||||
|
|
||||||
@OptIn(ExperimentalSerializationApi::class)
|
|
||||||
fun unrefSerializersModule(
|
fun unrefSerializersModule(
|
||||||
refCache: List<RefEntry>
|
refCache: List<RefEntry>,
|
||||||
): SerializersModule = SerializersModule {
|
): SerializersModule = SerializersModule {
|
||||||
|
|
||||||
contextual(TObjArray::class) {
|
contextual(TObjArray::class) {
|
||||||
@ -197,11 +197,13 @@ private object RootDecoder {
|
|||||||
fillCache(it)
|
fillCache(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is JsonArray -> {
|
is JsonArray -> {
|
||||||
element.forEach {
|
element.forEach {
|
||||||
fillCache(it)
|
fillCache(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
//ignore primitives
|
//ignore primitives
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package ru.mipt.npm.root.serialization
|
|||||||
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.visionforge.MutableVisionContainer
|
||||||
import space.kscience.visionforge.solid.*
|
import space.kscience.visionforge.solid.*
|
||||||
import kotlin.math.PI
|
import kotlin.math.PI
|
||||||
import kotlin.math.atan2
|
import kotlin.math.atan2
|
||||||
@ -132,7 +133,7 @@ private fun buildGroup(volume: TGeoVolume): SolidGroup {
|
|||||||
return if (volume is TGeoVolumeAssemblyRef) {
|
return if (volume is TGeoVolumeAssemblyRef) {
|
||||||
buildGroup(volume.value)
|
buildGroup(volume.value)
|
||||||
} else {
|
} else {
|
||||||
SolidGroup {
|
SolidGroup().apply {
|
||||||
volume.fShape?.let { addShape(it) }
|
volume.fShape?.let { addShape(it) }
|
||||||
volume.fNodes?.let {
|
volume.fNodes?.let {
|
||||||
it.arr.forEach { obj ->
|
it.arr.forEach { obj ->
|
||||||
@ -160,7 +161,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()}]")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,8 +181,8 @@ private fun SolidGroup.volume(volume: TGeoVolume, name: String? = null, cache: B
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
|
|
||||||
public fun TGeoManager.toSolid(): SolidGroup = SolidGroup {
|
public fun MutableVisionContainer<Solid>.rootGeo(tGeoManager: TGeoManager): SolidGroup = solidGroup {
|
||||||
fNodes.arr.forEach {
|
tGeoManager.fNodes.arr.forEach {
|
||||||
node(it)
|
node(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,8 +1,8 @@
|
|||||||
import ru.mipt.npm.gradle.DependencyConfiguration
|
import space.kscience.gradle.DependencyConfiguration
|
||||||
import ru.mipt.npm.gradle.FXModule
|
import space.kscience.gradle.FXModule
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("ru.mipt.npm.gradle.mpp")
|
id("space.kscience.gradle.mpp")
|
||||||
application
|
application
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,7 +36,7 @@ kotlin {
|
|||||||
jvmMain {
|
jvmMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":visionforge-fx"))
|
implementation(project(":visionforge-fx"))
|
||||||
implementation("ch.qos.logback:logback-classic:1.2.5")
|
implementation("ch.qos.logback:logback-classic:1.2.11")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
jsMain {
|
jsMain {
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
package space.kscience.visionforge.gdml
|
package space.kscience.visionforge.gdml
|
||||||
|
|
||||||
|
import space.kscience.dataforge.meta.asValue
|
||||||
|
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.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.getChild
|
||||||
import space.kscience.visionforge.get
|
|
||||||
import space.kscience.visionforge.setProperty
|
|
||||||
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 +18,8 @@ class GDMLVisionTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testCubesStyles(){
|
fun testCubesStyles(){
|
||||||
val segment = cubes["composite-000.segment-0"] as Solid
|
val segment = cubes.children.getChild("composite-000.segment-0") as Solid
|
||||||
println(segment.computeProperties().getValue(Vision.STYLE_KEY))
|
println(segment.properties.getValue(Vision.STYLE_KEY))
|
||||||
// println(segment.computePropertyNode(SolidMaterial.MATERIAL_KEY))
|
// println(segment.computePropertyNode(SolidMaterial.MATERIAL_KEY))
|
||||||
// println(segment.computeProperty(SolidMaterial.MATERIAL_COLOR_KEY))
|
// println(segment.computeProperty(SolidMaterial.MATERIAL_COLOR_KEY))
|
||||||
|
|
||||||
@ -35,7 +33,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.properties.setValue(SolidMaterial.MATERIAL_COLOR_KEY, "red".asValue())
|
||||||
assertEquals("red", child.getPropertyValue(SolidMaterial.MATERIAL_COLOR_KEY)?.string)
|
assertEquals("red", child.properties.getProperty(SolidMaterial.MATERIAL_COLOR_KEY).string)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -10,13 +10,11 @@ import org.w3c.files.get
|
|||||||
import react.Props
|
import react.Props
|
||||||
import react.dom.h2
|
import react.dom.h2
|
||||||
import react.fc
|
import react.fc
|
||||||
import react.useMemo
|
|
||||||
import react.useState
|
import react.useState
|
||||||
import space.kscience.dataforge.context.Context
|
|
||||||
import space.kscience.dataforge.context.fetch
|
|
||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.Name
|
||||||
import space.kscience.gdml.Gdml
|
import space.kscience.gdml.Gdml
|
||||||
import space.kscience.gdml.decodeFromString
|
import space.kscience.gdml.decodeFromString
|
||||||
|
import space.kscience.visionforge.Colors
|
||||||
import space.kscience.visionforge.gdml.markLayers
|
import space.kscience.visionforge.gdml.markLayers
|
||||||
import space.kscience.visionforge.gdml.toVision
|
import space.kscience.visionforge.gdml.toVision
|
||||||
import space.kscience.visionforge.ring.ThreeCanvasWithControls
|
import space.kscience.visionforge.ring.ThreeCanvasWithControls
|
||||||
@ -24,18 +22,19 @@ import space.kscience.visionforge.ring.tab
|
|||||||
import space.kscience.visionforge.setAsRoot
|
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.set
|
||||||
import styled.css
|
import styled.css
|
||||||
import styled.styledDiv
|
import styled.styledDiv
|
||||||
|
|
||||||
external interface GDMLAppProps : Props {
|
external interface GDMLAppProps : Props {
|
||||||
var context: Context
|
var solids: Solids
|
||||||
var vision: Solid?
|
var vision: Solid?
|
||||||
var selected: Name?
|
var selected: Name?
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsExport
|
@JsExport
|
||||||
val GDMLApp = fc<GDMLAppProps>("GDMLApp") { props ->
|
val GDMLApp = fc<GDMLAppProps>("GDMLApp") { props ->
|
||||||
val visionManager = useMemo(props.context) { props.context.fetch(Solids).visionManager }
|
|
||||||
var deferredVision: Deferred<Solid?> by useState {
|
var deferredVision: Deferred<Solid?> by useState {
|
||||||
CompletableDeferred(props.vision)
|
CompletableDeferred(props.vision)
|
||||||
}
|
}
|
||||||
@ -50,12 +49,15 @@ val GDMLApp = fc<GDMLAppProps>("GDMLApp") { props ->
|
|||||||
name.endsWith(".gdml") || name.endsWith(".xml") -> {
|
name.endsWith(".gdml") || name.endsWith(".xml") -> {
|
||||||
val gdml = Gdml.decodeFromString(data)
|
val gdml = Gdml.decodeFromString(data)
|
||||||
gdml.toVision().apply {
|
gdml.toVision().apply {
|
||||||
setAsRoot(visionManager)
|
setAsRoot(props.solids.visionManager)
|
||||||
console.info("Marking layers for file $name")
|
console.info("Marking layers for file $name")
|
||||||
markLayers()
|
markLayers()
|
||||||
|
ambientLight {
|
||||||
|
color.set(Colors.white)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
name.endsWith(".json") -> visionManager.decodeFromString(data)
|
name.endsWith(".json") -> props.solids.visionManager.decodeFromString(data)
|
||||||
else -> {
|
else -> {
|
||||||
window.alert("File extension is not recognized: $name")
|
window.alert("File extension is not recognized: $name")
|
||||||
error("File extension is not recognized: $name")
|
error("File extension is not recognized: $name")
|
||||||
@ -76,7 +78,7 @@ val GDMLApp = fc<GDMLAppProps>("GDMLApp") { props ->
|
|||||||
}
|
}
|
||||||
child(ThreeCanvasWithControls) {
|
child(ThreeCanvasWithControls) {
|
||||||
attrs {
|
attrs {
|
||||||
this.context = props.context
|
this.solids = props.solids
|
||||||
this.builderOfSolid = deferredVision
|
this.builderOfSolid = deferredVision
|
||||||
this.selected = props.selected
|
this.selected = props.selected
|
||||||
tab("Load") {
|
tab("Load") {
|
||||||
|
@ -2,11 +2,17 @@ package space.kscience.visionforge.gdml.demo
|
|||||||
|
|
||||||
import kotlinx.browser.document
|
import kotlinx.browser.document
|
||||||
import kotlinx.css.*
|
import kotlinx.css.*
|
||||||
import react.dom.render
|
import react.dom.client.createRoot
|
||||||
import space.kscience.dataforge.context.Context
|
import space.kscience.dataforge.context.Context
|
||||||
|
import space.kscience.dataforge.context.fetch
|
||||||
import space.kscience.gdml.GdmlShowCase
|
import space.kscience.gdml.GdmlShowCase
|
||||||
import space.kscience.visionforge.Application
|
import space.kscience.visionforge.Application
|
||||||
|
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.solid.Solids
|
||||||
|
import space.kscience.visionforge.solid.ambientLight
|
||||||
|
import space.kscience.visionforge.solid.set
|
||||||
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
|
||||||
@ -39,12 +45,16 @@ private class GDMLDemoApp : Application {
|
|||||||
|
|
||||||
val element = document.getElementById("application") ?: error("Element with id 'application' not found on page")
|
val element = document.getElementById("application") ?: error("Element with id 'application' not found on page")
|
||||||
|
|
||||||
render(element) {
|
createRoot(element).render {
|
||||||
child(GDMLApp) {
|
child(GDMLApp) {
|
||||||
val vision = GdmlShowCase.cubes().toVision()
|
val vision = GdmlShowCase.cubes().toVision().apply {
|
||||||
|
ambientLight {
|
||||||
|
color.set(Colors.white)
|
||||||
|
}
|
||||||
|
}
|
||||||
//println(context.plugins.fetch(VisionManager).encodeToString(vision))
|
//println(context.plugins.fetch(VisionManager).encodeToString(vision))
|
||||||
attrs {
|
attrs {
|
||||||
this.context = context
|
this.solids = context.fetch(Solids)
|
||||||
this.vision = vision
|
this.vision = vision
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id("ru.mipt.npm.gradle.js")
|
id("space.kscience.gradle.js")
|
||||||
}
|
}
|
||||||
|
|
||||||
kscience{
|
kscience{
|
||||||
|
@ -1,15 +1,17 @@
|
|||||||
import kotlinx.browser.document
|
import kotlinx.browser.document
|
||||||
import kotlinx.css.*
|
import kotlinx.css.*
|
||||||
import react.child
|
import react.dom.client.createRoot
|
||||||
import react.dom.render
|
|
||||||
import ringui.SmartTabs
|
import ringui.SmartTabs
|
||||||
import ringui.Tab
|
import ringui.Tab
|
||||||
import space.kscience.dataforge.context.Context
|
import space.kscience.dataforge.context.Context
|
||||||
|
import space.kscience.dataforge.context.fetch
|
||||||
import space.kscience.plotly.models.Trace
|
import space.kscience.plotly.models.Trace
|
||||||
import space.kscience.plotly.scatter
|
import space.kscience.plotly.scatter
|
||||||
import space.kscience.visionforge.Application
|
import space.kscience.visionforge.Application
|
||||||
|
import space.kscience.visionforge.Colors
|
||||||
import space.kscience.visionforge.VisionClient
|
import space.kscience.visionforge.VisionClient
|
||||||
import space.kscience.visionforge.plotly.PlotlyPlugin
|
import space.kscience.visionforge.plotly.PlotlyPlugin
|
||||||
|
import space.kscience.visionforge.react.render
|
||||||
import space.kscience.visionforge.ring.ThreeCanvasWithControls
|
import space.kscience.visionforge.ring.ThreeCanvasWithControls
|
||||||
import space.kscience.visionforge.ring.ThreeWithControlsPlugin
|
import space.kscience.visionforge.ring.ThreeWithControlsPlugin
|
||||||
import space.kscience.visionforge.ring.solid
|
import space.kscience.visionforge.ring.solid
|
||||||
@ -38,7 +40,7 @@ private class JsPlaygroundApp : Application {
|
|||||||
|
|
||||||
val element = document.getElementById("playground") ?: error("Element with id 'playground' not found on page")
|
val element = document.getElementById("playground") ?: error("Element with id 'playground' not found on page")
|
||||||
|
|
||||||
render(element) {
|
createRoot(element).render {
|
||||||
styledDiv {
|
styledDiv {
|
||||||
css {
|
css {
|
||||||
padding(0.pt)
|
padding(0.pt)
|
||||||
@ -48,9 +50,9 @@ private class JsPlaygroundApp : Application {
|
|||||||
}
|
}
|
||||||
SmartTabs("gravity") {
|
SmartTabs("gravity") {
|
||||||
Tab("gravity") {
|
Tab("gravity") {
|
||||||
GravityDemo{
|
GravityDemo {
|
||||||
attrs {
|
attrs {
|
||||||
this.context = playgroundContext
|
this.solids = playgroundContext.fetch(Solids)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -71,15 +73,18 @@ private class JsPlaygroundApp : Application {
|
|||||||
child(ThreeCanvasWithControls) {
|
child(ThreeCanvasWithControls) {
|
||||||
val random = Random(112233)
|
val random = Random(112233)
|
||||||
attrs {
|
attrs {
|
||||||
context = playgroundContext
|
solids = playgroundContext.fetch(Solids)
|
||||||
solid {
|
solid {
|
||||||
|
ambientLight {
|
||||||
|
color.set(Colors.white)
|
||||||
|
}
|
||||||
repeat(100) {
|
repeat(100) {
|
||||||
sphere(5, name = "sphere[$it]") {
|
sphere(5, name = "sphere[$it]") {
|
||||||
x = random.nextDouble(-300.0, 300.0)
|
x = random.nextDouble(-300.0, 300.0)
|
||||||
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
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,9 @@ import kotlinx.coroutines.launch
|
|||||||
import kotlinx.css.*
|
import kotlinx.css.*
|
||||||
import react.Props
|
import react.Props
|
||||||
import react.fc
|
import react.fc
|
||||||
import space.kscience.dataforge.context.Context
|
|
||||||
import space.kscience.plotly.layout
|
import space.kscience.plotly.layout
|
||||||
import space.kscience.plotly.models.Trace
|
import space.kscience.plotly.models.Trace
|
||||||
|
import space.kscience.visionforge.Colors
|
||||||
import space.kscience.visionforge.markup.VisionOfMarkup
|
import space.kscience.visionforge.markup.VisionOfMarkup
|
||||||
import space.kscience.visionforge.react.flexRow
|
import space.kscience.visionforge.react.flexRow
|
||||||
import space.kscience.visionforge.ring.ThreeCanvasWithControls
|
import space.kscience.visionforge.ring.ThreeCanvasWithControls
|
||||||
@ -17,14 +17,14 @@ import styled.styledDiv
|
|||||||
import kotlin.math.sqrt
|
import kotlin.math.sqrt
|
||||||
|
|
||||||
external interface DemoProps : Props {
|
external interface DemoProps : Props {
|
||||||
var context: Context
|
var solids: Solids
|
||||||
}
|
}
|
||||||
|
|
||||||
val GravityDemo = fc<DemoProps> { props ->
|
val GravityDemo = fc<DemoProps> { props ->
|
||||||
val velocityTrace = Trace{
|
val velocityTrace = Trace {
|
||||||
name = "velocity"
|
name = "velocity"
|
||||||
}
|
}
|
||||||
val energyTrace = Trace{
|
val energyTrace = Trace {
|
||||||
name = "energy"
|
name = "energy"
|
||||||
}
|
}
|
||||||
val markup = VisionOfMarkup()
|
val markup = VisionOfMarkup()
|
||||||
@ -39,14 +39,19 @@ val GravityDemo = fc<DemoProps> { props ->
|
|||||||
}
|
}
|
||||||
child(ThreeCanvasWithControls) {
|
child(ThreeCanvasWithControls) {
|
||||||
attrs {
|
attrs {
|
||||||
context = props.context
|
solids = props.solids
|
||||||
solid {
|
solid {
|
||||||
|
pointLight(200, 200, 200, name = "light"){
|
||||||
|
color.set(Colors.white)
|
||||||
|
}
|
||||||
|
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 {
|
solids.context.launch {
|
||||||
val g = 10.0
|
val g = 10.0
|
||||||
val dt = 0.1
|
val dt = 0.1
|
||||||
var time = 0.0
|
var time = 0.0
|
||||||
@ -91,7 +96,7 @@ val GravityDemo = fc<DemoProps> { props ->
|
|||||||
height = 50.vh - 50.pt
|
height = 50.vh - 50.pt
|
||||||
}
|
}
|
||||||
plotly {
|
plotly {
|
||||||
traces(velocityTrace,energyTrace)
|
traces(velocityTrace, energyTrace)
|
||||||
layout {
|
layout {
|
||||||
xaxis.title = "time"
|
xaxis.title = "time"
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id("ru.mipt.npm.gradle.mpp")
|
id("space.kscience.gradle.mpp")
|
||||||
application
|
application
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -21,21 +21,13 @@ kotlin {
|
|||||||
useCommonJs()
|
useCommonJs()
|
||||||
browser {
|
browser {
|
||||||
commonWebpackConfig {
|
commonWebpackConfig {
|
||||||
cssSupport.enabled = false
|
cssSupport {
|
||||||
|
enabled = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
afterEvaluate {
|
|
||||||
val jsBrowserDistribution by tasks.getting
|
|
||||||
|
|
||||||
tasks.getByName<ProcessResources>("jvmProcessResources") {
|
|
||||||
dependsOn(jsBrowserDistribution)
|
|
||||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
|
||||||
from(jsBrowserDistribution)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
commonMain {
|
commonMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
@ -45,8 +37,10 @@ kotlin {
|
|||||||
jvmMain {
|
jvmMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("org.apache.commons:commons-math3:3.6.1")
|
implementation("org.apache.commons:commons-math3:3.6.1")
|
||||||
implementation(npmlibs.ktor.server.cio)
|
implementation("io.ktor:ktor-server-cio:${ktorVersion}")
|
||||||
implementation(npmlibs.ktor.serialization)
|
implementation("io.ktor:ktor-server-content-negotiation:${ktorVersion}")
|
||||||
|
implementation("io.ktor:ktor-serialization-kotlinx-json:${ktorVersion}")
|
||||||
|
implementation("ch.qos.logback:logback-classic:1.2.11")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
jsMain {
|
jsMain {
|
||||||
@ -63,6 +57,23 @@ application {
|
|||||||
mainClass.set("ru.mipt.npm.muon.monitor.server.MMServerKt")
|
mainClass.set("ru.mipt.npm.muon.monitor.server.MMServerKt")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val jsBrowserDistribution by tasks.getting
|
||||||
|
val jsBrowserDevelopmentExecutableDistribution by tasks.getting
|
||||||
|
|
||||||
|
val devMode = rootProject.findProperty("visionforge.development") as? Boolean
|
||||||
|
?: rootProject.version.toString().contains("dev")
|
||||||
|
|
||||||
|
tasks.getByName<ProcessResources>("jvmProcessResources") {
|
||||||
|
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||||
|
if (devMode) {
|
||||||
|
dependsOn(jsBrowserDevelopmentExecutableDistribution)
|
||||||
|
from(jsBrowserDevelopmentExecutableDistribution)
|
||||||
|
} else {
|
||||||
|
dependsOn(jsBrowserDistribution)
|
||||||
|
from(jsBrowserDistribution)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//distributions {
|
//distributions {
|
||||||
// main {
|
// main {
|
||||||
// contents {
|
// contents {
|
||||||
|
@ -3,19 +3,19 @@ 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.MutableVisionContainer
|
||||||
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.set
|
||||||
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 MutableVisionContainer<Solid>.pixel(pixel: SC1) {
|
||||||
val group = group(pixel.name) {
|
val group = solidGroup(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)
|
||||||
label(pixel.name) {
|
label(pixel.name) {
|
||||||
@ -27,7 +27,7 @@ class Model(val manager: VisionManager) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun SolidGroup.detector(detector: SC16) {
|
private fun SolidGroup.detector(detector: SC16) {
|
||||||
group(detector.name) {
|
solidGroup(detector.name) {
|
||||||
detector.pixels.forEach {
|
detector.pixels.forEach {
|
||||||
pixel(it)
|
pixel(it)
|
||||||
}
|
}
|
||||||
@ -39,40 +39,39 @@ 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 {
|
||||||
wireframe
|
color.set("darkgreen")
|
||||||
color("darkgreen")
|
|
||||||
}
|
}
|
||||||
rotationX = PI / 2
|
rotationX = PI / 2
|
||||||
group("bottom") {
|
solidGroup("bottom") {
|
||||||
Monitor.detectors.filter { it.center.z == LOWER_LAYER_Z }.forEach {
|
Monitor.detectors.filter { it.center.z == LOWER_LAYER_Z }.forEach {
|
||||||
detector(it)
|
detector(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
group("middle") {
|
solidGroup("middle") {
|
||||||
Monitor.detectors.filter { it.center.z == CENTRAL_LAYER_Z }.forEach {
|
Monitor.detectors.filter { it.center.z == CENTRAL_LAYER_Z }.forEach {
|
||||||
detector(it)
|
detector(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
group("top") {
|
solidGroup("top") {
|
||||||
Monitor.detectors.filter { it.center.z == UPPER_LAYER_Z }.forEach {
|
Monitor.detectors.filter { it.center.z == UPPER_LAYER_Z }.forEach {
|
||||||
detector(it)
|
detector(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tracks = group("tracks")
|
tracks = solidGroup("tracks")
|
||||||
}
|
}
|
||||||
|
|
||||||
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.properties.setProperty(SolidMaterial.MATERIAL_COLOR_KEY, null)
|
||||||
}
|
}
|
||||||
tracks.removeAll()
|
tracks.children.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun displayEvent(event: Event) {
|
fun displayEvent(event: Event) {
|
||||||
@ -84,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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,13 +16,16 @@ import react.dom.p
|
|||||||
import react.fc
|
import react.fc
|
||||||
import react.useMemo
|
import react.useMemo
|
||||||
import react.useState
|
import react.useState
|
||||||
import space.kscience.dataforge.context.Context
|
import space.kscience.dataforge.meta.invoke
|
||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.Name
|
||||||
|
import space.kscience.visionforge.Colors
|
||||||
import space.kscience.visionforge.react.flexColumn
|
import space.kscience.visionforge.react.flexColumn
|
||||||
import space.kscience.visionforge.react.flexRow
|
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.specifications.Camera
|
import space.kscience.visionforge.solid.Solids
|
||||||
|
import space.kscience.visionforge.solid.ambientLight
|
||||||
|
import space.kscience.visionforge.solid.set
|
||||||
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
|
||||||
@ -32,7 +35,7 @@ import kotlin.math.PI
|
|||||||
|
|
||||||
external interface MMAppProps : Props {
|
external interface MMAppProps : Props {
|
||||||
var model: Model
|
var model: Model
|
||||||
var context: Context
|
var solids: Solids
|
||||||
var selected: Name?
|
var selected: Name?
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,17 +45,21 @@ val MMApp = fc<MMAppProps>("Muon monitor") { props ->
|
|||||||
|
|
||||||
val mmOptions = useMemo {
|
val mmOptions = useMemo {
|
||||||
Canvas3DOptions {
|
Canvas3DOptions {
|
||||||
camera = Camera {
|
camera {
|
||||||
distance = 2100.0
|
distance = 2100.0
|
||||||
latitude = PI / 6
|
latitude = PI / 6
|
||||||
azimuth = PI + PI / 6
|
azimuth = PI + PI / 6
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val root = useMemo(props.model) {
|
val root = useMemo(props.model) {
|
||||||
props.model.root.apply {
|
props.model.root.apply {
|
||||||
edges()
|
edges()
|
||||||
|
ambientLight{
|
||||||
|
color.set(Colors.white)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,7 +71,7 @@ val MMApp = fc<MMAppProps>("Muon monitor") { props ->
|
|||||||
}
|
}
|
||||||
child(ThreeCanvasWithControls) {
|
child(ThreeCanvasWithControls) {
|
||||||
attrs {
|
attrs {
|
||||||
this.context = props.context
|
this.solids = props.solids
|
||||||
this.builderOfSolid = CompletableDeferred(root)
|
this.builderOfSolid = CompletableDeferred(root)
|
||||||
this.selected = props.selected
|
this.selected = props.selected
|
||||||
this.options = mmOptions
|
this.options = mmOptions
|
||||||
@ -75,7 +82,7 @@ val MMApp = fc<MMAppProps>("Muon monitor") { props ->
|
|||||||
+"Next"
|
+"Next"
|
||||||
attrs {
|
attrs {
|
||||||
onClickFunction = {
|
onClickFunction = {
|
||||||
context.launch {
|
solids.context.launch {
|
||||||
val event = window.fetch(
|
val event = window.fetch(
|
||||||
"http://localhost:8080/event",
|
"http://localhost:8080/event",
|
||||||
RequestInit("GET")
|
RequestInit("GET")
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
package ru.mipt.npm.muon.monitor
|
package ru.mipt.npm.muon.monitor
|
||||||
|
|
||||||
import kotlinx.browser.document
|
import kotlinx.browser.document
|
||||||
import react.dom.render
|
import react.dom.client.createRoot
|
||||||
import space.kscience.dataforge.context.Context
|
import space.kscience.dataforge.context.Context
|
||||||
import space.kscience.dataforge.context.fetch
|
import space.kscience.dataforge.context.fetch
|
||||||
import space.kscience.visionforge.Application
|
import space.kscience.visionforge.Application
|
||||||
import space.kscience.visionforge.VisionManager
|
import space.kscience.visionforge.VisionManager
|
||||||
|
import space.kscience.visionforge.react.render
|
||||||
|
import space.kscience.visionforge.solid.Solids
|
||||||
import space.kscience.visionforge.solid.three.ThreePlugin
|
import space.kscience.visionforge.solid.three.ThreePlugin
|
||||||
import space.kscience.visionforge.startApplication
|
import space.kscience.visionforge.startApplication
|
||||||
|
|
||||||
@ -16,16 +18,17 @@ private class MMDemoApp : Application {
|
|||||||
val context = Context("MM-demo") {
|
val context = Context("MM-demo") {
|
||||||
plugin(ThreePlugin)
|
plugin(ThreePlugin)
|
||||||
}
|
}
|
||||||
|
|
||||||
val visionManager = context.fetch(VisionManager)
|
val visionManager = context.fetch(VisionManager)
|
||||||
|
|
||||||
val model = Model(visionManager)
|
val model = Model(visionManager)
|
||||||
|
|
||||||
val element = document.getElementById("app") ?: error("Element with id 'app' not found on page")
|
val element = document.getElementById("app") ?: error("Element with id 'app' not found on page")
|
||||||
render(element) {
|
createRoot(element).render {
|
||||||
child(MMApp) {
|
child(MMApp) {
|
||||||
attrs {
|
attrs {
|
||||||
this.model = model
|
this.model = model
|
||||||
this.context = context
|
this.solids = context.fetch(Solids)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
<!-- <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">-->
|
<!-- <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">-->
|
||||||
<title>Three js demo for particle physics</title>
|
<title>Three js demo for particle physics</title>
|
||||||
<script type="text/javascript" src="muon-monitor.js"></script>
|
<script type="text/javascript" src="muon-monitor.js"></script>
|
||||||
<link rel="stylesheet" href="css/custom-bootstrap.css">
|
|
||||||
</head>
|
</head>
|
||||||
<body class="application">
|
<body class="application">
|
||||||
<div class="container-fluid max-vh-100" id = "app"> </div>
|
<div class="container-fluid max-vh-100" id = "app"> </div>
|
||||||
|
@ -1,24 +1,22 @@
|
|||||||
package ru.mipt.npm.muon.monitor.server
|
package ru.mipt.npm.muon.monitor.server
|
||||||
|
|
||||||
|
|
||||||
import io.ktor.application.Application
|
|
||||||
import io.ktor.application.call
|
|
||||||
import io.ktor.application.install
|
|
||||||
import io.ktor.application.log
|
|
||||||
import io.ktor.features.CallLogging
|
|
||||||
import io.ktor.features.ContentNegotiation
|
|
||||||
import io.ktor.features.DefaultHeaders
|
|
||||||
import io.ktor.http.ContentType
|
import io.ktor.http.ContentType
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
import io.ktor.http.content.resources
|
import io.ktor.serialization.kotlinx.json.json
|
||||||
import io.ktor.http.content.static
|
import io.ktor.server.application.Application
|
||||||
import io.ktor.response.respond
|
import io.ktor.server.application.call
|
||||||
import io.ktor.response.respondText
|
import io.ktor.server.application.install
|
||||||
import io.ktor.routing.Routing
|
import io.ktor.server.application.log
|
||||||
import io.ktor.routing.get
|
|
||||||
import io.ktor.serialization.json
|
|
||||||
import io.ktor.server.cio.CIO
|
import io.ktor.server.cio.CIO
|
||||||
import io.ktor.server.engine.embeddedServer
|
import io.ktor.server.engine.embeddedServer
|
||||||
|
import io.ktor.server.http.content.resources
|
||||||
|
import io.ktor.server.http.content.static
|
||||||
|
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
|
||||||
|
import io.ktor.server.response.respond
|
||||||
|
import io.ktor.server.response.respondText
|
||||||
|
import io.ktor.server.routing.Routing
|
||||||
|
import io.ktor.server.routing.get
|
||||||
import org.apache.commons.math3.random.JDKRandomGenerator
|
import org.apache.commons.math3.random.JDKRandomGenerator
|
||||||
import ru.mipt.npm.muon.monitor.Model
|
import ru.mipt.npm.muon.monitor.Model
|
||||||
import ru.mipt.npm.muon.monitor.sim.Cos2TrackGenerator
|
import ru.mipt.npm.muon.monitor.sim.Cos2TrackGenerator
|
||||||
@ -40,8 +38,6 @@ fun Application.module(context: Context = Global) {
|
|||||||
environment.log.info("Current directory: $currentDir")
|
environment.log.info("Current directory: $currentDir")
|
||||||
val solidManager = context.fetch(Solids)
|
val solidManager = context.fetch(Solids)
|
||||||
|
|
||||||
install(DefaultHeaders)
|
|
||||||
install(CallLogging)
|
|
||||||
install(ContentNegotiation) {
|
install(ContentNegotiation) {
|
||||||
json()
|
json()
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ kotlin {
|
|||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
jvmTarget = "11"
|
jvmTarget = "11"
|
||||||
freeCompilerArgs =
|
freeCompilerArgs =
|
||||||
freeCompilerArgs + "-Xjvm-default=all" + "-Xopt-in=kotlin.RequiresOptIn" + "-Xlambdas=indy"
|
freeCompilerArgs + "-Xjvm-default=all" + "-Xopt-in=kotlin.RequiresOptIn" + "-Xlambdas=indy" + "-Xcontext-receivers"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
testRuns["test"].executionTask.configure {
|
testRuns["test"].executionTask.configure {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package space.kscience.visionforge.examples
|
package space.kscience.visionforge.examples
|
||||||
|
|
||||||
import kotlinx.html.h2
|
import kotlinx.html.h2
|
||||||
import space.kscience.dataforge.values.ValueType
|
import space.kscience.dataforge.meta.ValueType
|
||||||
import space.kscience.plotly.layout
|
import space.kscience.plotly.layout
|
||||||
import space.kscience.plotly.models.ScatterMode
|
import space.kscience.plotly.models.ScatterMode
|
||||||
import space.kscience.plotly.models.TextPosition
|
import space.kscience.plotly.models.TextPosition
|
||||||
|
@ -1,13 +1,20 @@
|
|||||||
package space.kscience.visionforge.examples
|
package space.kscience.visionforge.examples
|
||||||
|
|
||||||
import space.kscience.gdml.GdmlShowCase
|
import space.kscience.gdml.GdmlShowCase
|
||||||
|
import space.kscience.visionforge.Colors
|
||||||
import space.kscience.visionforge.gdml.toVision
|
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.ambientLight
|
||||||
|
import space.kscience.visionforge.solid.set
|
||||||
|
|
||||||
fun main() = makeVisionFile(resourceLocation = ResourceLocation.SYSTEM){
|
fun main() = makeVisionFile(resourceLocation = ResourceLocation.SYSTEM) {
|
||||||
vision("canvas") {
|
vision("canvas") {
|
||||||
requirePlugin(Solids)
|
requirePlugin(Solids)
|
||||||
GdmlShowCase.cubes().toVision()
|
GdmlShowCase.cubes().toVision().also {
|
||||||
|
it.ambientLight {
|
||||||
|
color.set(Colors.white)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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()
|
||||||
|
@ -1,12 +1,21 @@
|
|||||||
package space.kscience.visionforge.examples
|
package space.kscience.visionforge.examples
|
||||||
|
|
||||||
import space.kscience.gdml.GdmlShowCase
|
import space.kscience.gdml.GdmlShowCase
|
||||||
import space.kscience.visionforge.gdml.toVision
|
import space.kscience.visionforge.Colors
|
||||||
|
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.set
|
||||||
|
import space.kscience.visionforge.solid.solid
|
||||||
|
|
||||||
fun main() = makeVisionFile {
|
fun main() = makeVisionFile {
|
||||||
vision("canvas") {
|
vision("canvas") {
|
||||||
requirePlugin(Solids)
|
requirePlugin(Solids)
|
||||||
GdmlShowCase.babyIaxo().toVision()
|
solid {
|
||||||
|
ambientLight {
|
||||||
|
color.set(Colors.white)
|
||||||
|
}
|
||||||
|
gdml(GdmlShowCase.babyIaxo(), "D0")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,6 +2,7 @@ package space.kscience.visionforge.examples
|
|||||||
|
|
||||||
import kotlinx.html.div
|
import kotlinx.html.div
|
||||||
import kotlinx.html.h1
|
import kotlinx.html.h1
|
||||||
|
import space.kscience.visionforge.Colors
|
||||||
import space.kscience.visionforge.html.ResourceLocation
|
import space.kscience.visionforge.html.ResourceLocation
|
||||||
import space.kscience.visionforge.solid.*
|
import space.kscience.visionforge.solid.*
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
@ -17,13 +18,16 @@ fun main() = makeVisionFile(
|
|||||||
div {
|
div {
|
||||||
vision {
|
vision {
|
||||||
solid {
|
solid {
|
||||||
|
ambientLight {
|
||||||
|
color.set(Colors.white)
|
||||||
|
}
|
||||||
repeat(100) {
|
repeat(100) {
|
||||||
sphere(5, name = "sphere[$it]") {
|
sphere(5, name = "sphere[$it]") {
|
||||||
x = random.nextDouble(-300.0, 300.0)
|
x = random.nextDouble(-300.0, 300.0)
|
||||||
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
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
package space.kscience.visionforge.examples
|
package space.kscience.visionforge.examples
|
||||||
|
|
||||||
import ru.mipt.npm.root.DGeoManager
|
import ru.mipt.npm.root.DGeoManager
|
||||||
|
import ru.mipt.npm.root.rootGeo
|
||||||
import ru.mipt.npm.root.serialization.TGeoManager
|
import ru.mipt.npm.root.serialization.TGeoManager
|
||||||
import ru.mipt.npm.root.toSolid
|
|
||||||
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.isLeaf
|
import space.kscience.dataforge.meta.isLeaf
|
||||||
import space.kscience.dataforge.values.string
|
import space.kscience.dataforge.meta.string
|
||||||
import space.kscience.visionforge.solid.Solids
|
import space.kscience.visionforge.solid.Solids
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
import java.util.zip.ZipInputStream
|
import java.util.zip.ZipInputStream
|
||||||
@ -34,7 +34,7 @@ fun main() {
|
|||||||
println(it)
|
println(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
val solid = geo.toSolid()
|
val solid = Solids.rootGeo(geo)
|
||||||
|
|
||||||
Paths.get("BM@N.vf.json").writeText(Solids.encodeToString(solid))
|
Paths.get("BM@N.vf.json").writeText(Solids.encodeToString(solid))
|
||||||
//println(Solids.encodeToString(solid))
|
//println(Solids.encodeToString(solid))
|
||||||
|
@ -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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package space.kscience.visionforge.examples
|
package space.kscience.visionforge.examples
|
||||||
|
|
||||||
import space.kscience.dataforge.values.ValueType
|
import space.kscience.dataforge.meta.ValueType
|
||||||
import space.kscience.tables.ColumnHeader
|
import space.kscience.tables.ColumnHeader
|
||||||
import space.kscience.tables.valueRow
|
import space.kscience.tables.valueRow
|
||||||
import space.kscience.visionforge.html.ResourceLocation
|
import space.kscience.visionforge.html.ResourceLocation
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id("ru.mipt.npm.gradle.jvm")
|
id("space.kscience.gradle.jvm")
|
||||||
application
|
application
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -15,7 +15,7 @@ group = "ru.mipt.npm"
|
|||||||
|
|
||||||
dependencies{
|
dependencies{
|
||||||
implementation(project(":visionforge-threejs:visionforge-threejs-server"))
|
implementation(project(":visionforge-threejs:visionforge-threejs-server"))
|
||||||
implementation("ch.qos.logback:logback-classic:1.2.3")
|
implementation("ch.qos.logback:logback-classic:1.2.11")
|
||||||
}
|
}
|
||||||
|
|
||||||
application {
|
application {
|
||||||
|
@ -6,7 +6,7 @@ import space.kscience.visionforge.style
|
|||||||
import space.kscience.visionforge.useStyle
|
import space.kscience.visionforge.useStyle
|
||||||
import kotlin.math.PI
|
import kotlin.math.PI
|
||||||
|
|
||||||
internal fun visionOfSatellite(
|
internal fun Solids.visionOfSatellite(
|
||||||
layers: Int = 10,
|
layers: Int = 10,
|
||||||
layerHeight: Number = 10,
|
layerHeight: Number = 10,
|
||||||
xSegments: Int = 3,
|
xSegments: Int = 3,
|
||||||
@ -14,8 +14,8 @@ internal fun visionOfSatellite(
|
|||||||
xSegmentSize: Number = 30,
|
xSegmentSize: Number = 30,
|
||||||
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
|
||||||
}
|
}
|
||||||
@ -31,7 +31,7 @@ internal fun visionOfSatellite(
|
|||||||
val totalXSize = xSegments * xSegmentSize.toDouble()
|
val totalXSize = xSegments * xSegmentSize.toDouble()
|
||||||
val totalYSize = ySegments * ySegmentSize.toDouble()
|
val totalYSize = ySegments * ySegmentSize.toDouble()
|
||||||
for (layer in 1..layers) {
|
for (layer in 1..layers) {
|
||||||
group("layer[$layer]") {
|
solidGroup("layer[$layer]") {
|
||||||
for (i in 1..xSegments) {
|
for (i in 1..xSegments) {
|
||||||
for (j in 1..ySegments) {
|
for (j in 1..ySegments) {
|
||||||
box(xSegmentSize, ySegmentSize, layerHeight, name = "segment[$i,$j]") {
|
box(xSegmentSize, ySegmentSize, layerHeight, name = "segment[$i,$j]") {
|
||||||
@ -42,7 +42,7 @@ internal fun visionOfSatellite(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
group("fibers") {
|
solidGroup("fibers") {
|
||||||
for (i in 1..xSegments) {
|
for (i in 1..xSegments) {
|
||||||
cylinder(fiberDiameter, totalYSize) {
|
cylinder(fiberDiameter, totalYSize) {
|
||||||
useStyle(red)
|
useStyle(red)
|
||||||
|
@ -5,7 +5,10 @@ import kotlinx.coroutines.*
|
|||||||
import kotlinx.html.div
|
import kotlinx.html.div
|
||||||
import kotlinx.html.h1
|
import kotlinx.html.h1
|
||||||
import space.kscience.dataforge.context.Context
|
import space.kscience.dataforge.context.Context
|
||||||
|
import space.kscience.dataforge.context.fetch
|
||||||
|
import space.kscience.dataforge.misc.DFExperimental
|
||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.Name
|
||||||
|
import space.kscience.visionforge.Colors
|
||||||
import space.kscience.visionforge.html.Page
|
import space.kscience.visionforge.html.Page
|
||||||
import space.kscience.visionforge.html.plus
|
import space.kscience.visionforge.html.plus
|
||||||
import space.kscience.visionforge.server.close
|
import space.kscience.visionforge.server.close
|
||||||
@ -17,13 +20,20 @@ import space.kscience.visionforge.visionManager
|
|||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
|
|
||||||
|
@OptIn(DFExperimental::class)
|
||||||
fun main() {
|
fun main() {
|
||||||
val satContext = Context("sat") {
|
val satContext = Context("sat") {
|
||||||
plugin(Solids)
|
plugin(Solids)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val solids = satContext.fetch(Solids)
|
||||||
|
|
||||||
//Create a geometry
|
//Create a geometry
|
||||||
val sat = visionOfSatellite(ySegments = 3)
|
val sat = solids.visionOfSatellite(ySegments = 3).apply {
|
||||||
|
ambientLight {
|
||||||
|
color.set(Colors.white)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val server = satContext.visionManager.serve {
|
val server = satContext.visionManager.serve {
|
||||||
page(header = Page.threeJsHeader + Page.styleSheetHeader("css/styles.css")) {
|
page(header = Page.threeJsHeader + Page.styleSheetHeader("css/styles.css")) {
|
||||||
@ -44,7 +54,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)
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import ru.mipt.npm.gradle.DependencyConfiguration
|
import space.kscience.gradle.DependencyConfiguration
|
||||||
import ru.mipt.npm.gradle.FXModule
|
import space.kscience.gradle.FXModule
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("ru.mipt.npm.gradle.mpp")
|
id("space.kscience.gradle.mpp")
|
||||||
application
|
application
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,5 +40,5 @@ kotlin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
application {
|
application {
|
||||||
mainClassName = "space.kscience.visionforge.solid.demo.FXDemoAppKt"
|
mainClass.set("space.kscience.visionforge.solid.demo.FXDemoAppKt")
|
||||||
}
|
}
|
@ -3,7 +3,10 @@ package space.kscience.visionforge.solid.demo
|
|||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.Meta
|
||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.Name
|
||||||
import space.kscience.visionforge.Vision
|
import space.kscience.visionforge.Vision
|
||||||
|
import space.kscience.visionforge.solid.Solids
|
||||||
|
|
||||||
public interface VisionLayout<in V: Vision> {
|
public interface VisionLayout<in V: Vision> {
|
||||||
|
val solids: Solids
|
||||||
|
|
||||||
public fun render(name: Name, vision: V, meta: Meta = Meta.EMPTY)
|
public fun render(name: Name, vision: V, meta: Meta = Meta.EMPTY)
|
||||||
}
|
}
|
@ -18,7 +18,12 @@ fun VisionLayout<Solid>.demo(name: String, title: String = name, block: SolidGro
|
|||||||
val meta = Meta {
|
val meta = Meta {
|
||||||
"title" put title
|
"title" put title
|
||||||
}
|
}
|
||||||
val vision = SolidGroup(block)
|
val vision = solids.solidGroup {
|
||||||
|
block()
|
||||||
|
ambientLight {
|
||||||
|
color.set(Colors.white)
|
||||||
|
}
|
||||||
|
}
|
||||||
render(Name.parse(name), vision, meta)
|
render(Name.parse(name), vision, meta)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,30 +44,31 @@ val canvasOptions = Canvas3DOptions {
|
|||||||
@OptIn(DelicateCoroutinesApi::class)
|
@OptIn(DelicateCoroutinesApi::class)
|
||||||
fun VisionLayout<Solid>.showcase() {
|
fun VisionLayout<Solid>.showcase() {
|
||||||
demo("shapes", "Basic shapes") {
|
demo("shapes", "Basic shapes") {
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
demo("dynamic", "Dynamic properties") {
|
demo("dynamic", "Dynamic properties") {
|
||||||
val group = group {
|
val group = solidGroup {
|
||||||
box(100, 100, 100) {
|
box(100, 100, 100) {
|
||||||
z = 110.0
|
z = 110.0
|
||||||
opacity = 0.5
|
opacity = 0.5
|
||||||
@ -72,7 +78,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) {
|
||||||
@ -87,19 +93,19 @@ 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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
demo("rotation", "Rotations") {
|
demo("rotation", "Rotations") {
|
||||||
box(100, 100, 100)
|
box(100, 100, 100)
|
||||||
group {
|
solidGroup {
|
||||||
x = 200
|
x = 200
|
||||||
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -112,7 +118,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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -121,13 +127,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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -154,7 +160,7 @@ fun VisionLayout<Solid>.showcaseCSG() {
|
|||||||
detail = 32
|
detail = 32
|
||||||
}
|
}
|
||||||
material {
|
material {
|
||||||
color(Colors.pink)
|
color.set(Colors.pink)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
composite(CompositeType.UNION) {
|
composite(CompositeType.UNION) {
|
||||||
@ -164,7 +170,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) {
|
||||||
@ -175,7 +181,7 @@ fun VisionLayout<Solid>.showcaseCSG() {
|
|||||||
sphere(50) {
|
sphere(50) {
|
||||||
detail = 32
|
detail = 32
|
||||||
}
|
}
|
||||||
color("teal")
|
color.set("teal")
|
||||||
opacity = 0.7
|
opacity = 0.7
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -186,7 +192,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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ import space.kscience.dataforge.meta.get
|
|||||||
import space.kscience.dataforge.meta.string
|
import space.kscience.dataforge.meta.string
|
||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.Name
|
||||||
import space.kscience.visionforge.solid.Solid
|
import space.kscience.visionforge.solid.Solid
|
||||||
|
import space.kscience.visionforge.solid.Solids
|
||||||
import space.kscience.visionforge.solid.three.ThreeCanvas
|
import space.kscience.visionforge.solid.three.ThreeCanvas
|
||||||
import space.kscience.visionforge.solid.three.ThreePlugin
|
import space.kscience.visionforge.solid.three.ThreePlugin
|
||||||
|
|
||||||
@ -27,6 +28,8 @@ class ThreeDemoGrid(element: Element) : VisionLayout<Solid> {
|
|||||||
|
|
||||||
private val three = Global.fetch(ThreePlugin)
|
private val three = Global.fetch(ThreePlugin)
|
||||||
|
|
||||||
|
override val solids: Solids get() = three.solids
|
||||||
|
|
||||||
init {
|
init {
|
||||||
element.clear()
|
element.clear()
|
||||||
element.append {
|
element.append {
|
||||||
|
@ -1,20 +1,18 @@
|
|||||||
package space.kscience.visionforge.solid.demo
|
package space.kscience.visionforge.solid.demo
|
||||||
|
|
||||||
import info.laht.threekt.core.Object3D
|
import space.kscience.dataforge.meta.asValue
|
||||||
import info.laht.threekt.geometries.BoxGeometry
|
|
||||||
import info.laht.threekt.objects.Mesh
|
|
||||||
import space.kscience.dataforge.meta.get
|
|
||||||
import space.kscience.dataforge.meta.int
|
import space.kscience.dataforge.meta.int
|
||||||
import space.kscience.dataforge.meta.number
|
import space.kscience.dataforge.meta.number
|
||||||
import space.kscience.dataforge.names.asName
|
import space.kscience.dataforge.names.asName
|
||||||
import space.kscience.dataforge.names.startsWith
|
import space.kscience.dataforge.names.startsWith
|
||||||
import space.kscience.dataforge.values.asValue
|
|
||||||
import space.kscience.visionforge.onPropertyChange
|
import space.kscience.visionforge.onPropertyChange
|
||||||
import space.kscience.visionforge.set
|
import space.kscience.visionforge.setChild
|
||||||
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.*
|
||||||
|
import three.core.Object3D
|
||||||
|
import three.geometries.BoxGeometry
|
||||||
|
import three.objects.Mesh
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
|
|
||||||
internal fun SolidGroup.varBox(
|
internal fun SolidGroup.varBox(
|
||||||
@ -22,7 +20,7 @@ internal fun SolidGroup.varBox(
|
|||||||
ySize: Number,
|
ySize: Number,
|
||||||
name: String = "",
|
name: String = "",
|
||||||
action: VariableBox.() -> Unit = {},
|
action: VariableBox.() -> Unit = {},
|
||||||
): VariableBox = VariableBox(xSize, ySize).apply(action).also { set(name, it) }
|
): VariableBox = VariableBox(xSize, ySize).apply(action).also { setChild(name, it) }
|
||||||
|
|
||||||
internal class VariableBox(val xSize: Number, val ySize: Number) : ThreeJsVision() {
|
internal class VariableBox(val xSize: Number, val ySize: Number) : ThreeJsVision() {
|
||||||
|
|
||||||
@ -44,13 +42,13 @@ internal class VariableBox(val xSize: Number, val ySize: Number) : ThreeJsVision
|
|||||||
it.layers.enable(this@VariableBox.layer)
|
it.layers.enable(this@VariableBox.layer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mesh.scale.z = meta[VALUE].number?.toDouble() ?: 1.0
|
mesh.scale.z = properties.getValue(VALUE)?.number?.toDouble() ?: 1.0
|
||||||
|
|
||||||
//add listener to object properties
|
//add listener to object properties
|
||||||
onPropertyChange { name ->
|
onPropertyChange { name ->
|
||||||
when {
|
when {
|
||||||
name == VALUE -> {
|
name == VALUE -> {
|
||||||
val value = meta.get(VALUE).int ?: 0
|
val value = properties.getValue(VALUE)?.int ?: 0
|
||||||
val size = value.toFloat() / 255f * 20f
|
val size = value.toFloat() / 255f * 20f
|
||||||
mesh.scale.z = size.toDouble()
|
mesh.scale.z = size.toDouble()
|
||||||
mesh.position.z = size.toDouble() / 2
|
mesh.position.z = size.toDouble() / 2
|
||||||
@ -61,7 +59,8 @@ internal class VariableBox(val xSize: Number, val ySize: Number) : ThreeJsVision
|
|||||||
material.color.setRGB(r.toFloat() / 256, g.toFloat() / 256, b.toFloat() / 256)
|
material.color.setRGB(r.toFloat() / 256, g.toFloat() / 256, b.toFloat() / 256)
|
||||||
mesh.updateMatrix()
|
mesh.updateMatrix()
|
||||||
}
|
}
|
||||||
name.startsWith(MeshThreeFactory.EDGES_KEY) -> mesh.applyEdges(this@VariableBox)
|
|
||||||
|
name.startsWith(ThreeMeshFactory.EDGES_KEY) -> mesh.applyEdges(this@VariableBox)
|
||||||
else -> mesh.updateProperty(this@VariableBox, name)
|
else -> mesh.updateProperty(this@VariableBox, name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -70,9 +69,9 @@ internal class VariableBox(val xSize: Number, val ySize: Number) : ThreeJsVision
|
|||||||
}
|
}
|
||||||
|
|
||||||
var value: Int
|
var value: Int
|
||||||
get() = meta[VALUE].int ?: 0
|
get() = properties.getValue(VALUE)?.int ?: 0
|
||||||
set(value) {
|
set(value) {
|
||||||
setProperty(VALUE, value.asValue())
|
properties.setValue(VALUE, value.asValue())
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -10,9 +10,11 @@ import space.kscience.dataforge.names.Name
|
|||||||
import space.kscience.visionforge.solid.FX3DPlugin
|
import space.kscience.visionforge.solid.FX3DPlugin
|
||||||
import space.kscience.visionforge.solid.FXCanvas3D
|
import space.kscience.visionforge.solid.FXCanvas3D
|
||||||
import space.kscience.visionforge.solid.Solid
|
import space.kscience.visionforge.solid.Solid
|
||||||
|
import space.kscience.visionforge.solid.Solids
|
||||||
import tornadofx.*
|
import tornadofx.*
|
||||||
|
|
||||||
class FXDemoGrid : View(title = "DataForge-vis FX demo"), VisionLayout<Solid> {
|
class FXDemoGrid : View(title = "DataForge-vis FX demo"), VisionLayout<Solid> {
|
||||||
|
|
||||||
private val outputs = FXCollections.observableHashMap<Name, FXCanvas3D>()
|
private val outputs = FXCollections.observableHashMap<Name, FXCanvas3D>()
|
||||||
|
|
||||||
override val root: Parent = borderpane {
|
override val root: Parent = borderpane {
|
||||||
@ -24,6 +26,9 @@ class FXDemoGrid : View(title = "DataForge-vis FX demo"), VisionLayout<Solid> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val fx3d = Global.fetch(FX3DPlugin)
|
private val fx3d = Global.fetch(FX3DPlugin)
|
||||||
|
override val solids: Solids get() = fx3d.solids
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
override fun render(name: Name, vision: Solid, meta: Meta) {
|
override fun render(name: Name, vision: Solid, meta: Meta) {
|
||||||
outputs.getOrPut(name) { FXCanvas3D(fx3d, canvasOptions) }.render(vision)
|
outputs.getOrPut(name) { FXCanvas3D(fx3d, canvasOptions) }.render(vision)
|
||||||
|
@ -2,10 +2,10 @@ package space.kscience.visionforge.demo
|
|||||||
|
|
||||||
import javafx.geometry.Orientation
|
import javafx.geometry.Orientation
|
||||||
import space.kscience.dataforge.meta.MutableMeta
|
import space.kscience.dataforge.meta.MutableMeta
|
||||||
|
import space.kscience.dataforge.meta.ValueType
|
||||||
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||||
import space.kscience.dataforge.meta.descriptors.node
|
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.values.ValueType
|
|
||||||
import space.kscience.visionforge.editor.FXMetaModel
|
import space.kscience.visionforge.editor.FXMetaModel
|
||||||
import space.kscience.visionforge.editor.MetaViewer
|
import space.kscience.visionforge.editor.MetaViewer
|
||||||
import space.kscience.visionforge.editor.MutableMetaEditor
|
import space.kscience.visionforge.editor.MutableMetaEditor
|
||||||
|
@ -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.
|
||||||
|
@ -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
|
||||||
|
@ -59,7 +59,7 @@ box(10, 10, 10, name = "small box"){
|
|||||||
rotation = Point3D(0, 0, 0)
|
rotation = Point3D(0, 0, 0)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
![](../docs/images/small-box.png)
|
![](../images/small-box.png)
|
||||||
|
|
||||||
The `big box` will have properties with custom values.
|
The `big box` will have properties with custom values.
|
||||||
```kotlin
|
```kotlin
|
||||||
@ -72,7 +72,7 @@ box(40, 40, 40, name = "big box"){
|
|||||||
rotation = Point3D(60, 80, 0)
|
rotation = Point3D(60, 80, 0)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
![](../docs/images/big-rotated-box.png)
|
![](../images/big-rotated-box.png)
|
||||||
If we compare these boxes, we will see all differences.
|
If we compare these boxes, we will see all differences.
|
||||||
|
|
||||||
Here is the function `main` with both boxes.
|
Here is the function `main` with both boxes.
|
||||||
@ -111,8 +111,8 @@ fun main(){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
![](../docs/images/two-boxes-1.png)
|
![](../images/two-boxes-1.png)
|
||||||
![](../docs/images/two-boxes-2.png)
|
![](../images/two-boxes-2.png)
|
||||||
|
|
||||||
***There is plenty of other properties, especially those, which you can create by yourself. Here we mention just a small part.***
|
***There is plenty of other properties, especially those, which you can create by yourself. Here we mention just a small part.***
|
||||||
|
|
||||||
@ -142,8 +142,8 @@ polyline(Point3D(30, 20, 10), Point3D(30, -100, 30), Point3D(30, -100, 30), Poin
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
![](../docs/images/polyline-points.png)
|
![](../images/polyline-points.png)
|
||||||
![](../docs/images/polyline-points-2.png)
|
![](../images/polyline-points-2.png)
|
||||||
|
|
||||||
### 2) Box
|
### 2) Box
|
||||||
|
|
||||||
@ -165,7 +165,7 @@ Let's create just usual `box` with equal ribs.
|
|||||||
color("pink")
|
color("pink")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
![](../docs/images/box.png)
|
![](../images/box.png)
|
||||||
|
|
||||||
Now, let's make `box` with bigger `y` value.
|
Now, let's make `box` with bigger `y` value.
|
||||||
```kotlin
|
```kotlin
|
||||||
@ -175,7 +175,7 @@ Now, let's make `box` with bigger `y` value.
|
|||||||
```
|
```
|
||||||
As you can see, only the rib of `y-axis` differs from other ribs.
|
As you can see, only the rib of `y-axis` differs from other ribs.
|
||||||
|
|
||||||
![](../docs/images/high-box.png)
|
![](../images/high-box.png)
|
||||||
|
|
||||||
For a final trial, let's create a `box` with a bigger `x` value.
|
For a final trial, let's create a `box` with a bigger `x` value.
|
||||||
|
|
||||||
@ -189,7 +189,7 @@ For a final trial, let's create a `box` with a bigger `x` value.
|
|||||||
```
|
```
|
||||||
Predictably, only the `x-axis` rib is bigger than other ribs.
|
Predictably, only the `x-axis` rib is bigger than other ribs.
|
||||||
|
|
||||||
![](../docs/images/wide-box.png)
|
![](../images/wide-box.png)
|
||||||
|
|
||||||
### 3) Sphere
|
### 3) Sphere
|
||||||
|
|
||||||
@ -206,7 +206,7 @@ As for `radius`, it has `Float` type, and, as you can guess, it sets the radius
|
|||||||
color("blue")
|
color("blue")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
![](../docs/images/sphere.png)
|
![](../images/sphere.png)
|
||||||
|
|
||||||
### 4) Hexagon
|
### 4) Hexagon
|
||||||
|
|
||||||
@ -220,7 +220,7 @@ It is solid which has six edges. It is set by eight values: `node1`,..., `node8`
|
|||||||
5) Edge with vertices `node1`, `node5`, `node8`, `node4`
|
5) Edge with vertices `node1`, `node5`, `node8`, `node4`
|
||||||
6) Edge with vertices `node8`, `node5`, `node6`, `node7`
|
6) Edge with vertices `node8`, `node5`, `node6`, `node7`
|
||||||
|
|
||||||
![](../docs/images/scheme.png)
|
![](../images/scheme.png)
|
||||||
|
|
||||||
As the hexagon takes in specific points, we understand that this solid cannot be moved, it is fixed in space, and it can't make pivots.
|
As the hexagon takes in specific points, we understand that this solid cannot be moved, it is fixed in space, and it can't make pivots.
|
||||||
|
|
||||||
@ -239,7 +239,7 @@ Let's make classic parallelepiped.
|
|||||||
color("green")
|
color("green")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
![](../docs/images/classic-hexagon.png)
|
![](../images/classic-hexagon.png)
|
||||||
|
|
||||||
Now, let's make a custom hexagon.
|
Now, let's make a custom hexagon.
|
||||||
|
|
||||||
@ -258,7 +258,7 @@ hexagon(
|
|||||||
color("brown")
|
color("brown")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
![](../docs/images/custom-hexagon.png)
|
![](../images/custom-hexagon.png)
|
||||||
### 3) Cone
|
### 3) Cone
|
||||||
It takes in six values: `bottomRadius`, `height`, `upperRadius`, `startAngle`, `angle`, and `name`.
|
It takes in six values: `bottomRadius`, `height`, `upperRadius`, `startAngle`, `angle`, and `name`.
|
||||||
|
|
||||||
@ -274,8 +274,8 @@ Let's build a classic cone:
|
|||||||
color("beige")
|
color("beige")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
![](../docs/images/cone-1.png)
|
![](../images/cone-1.png)
|
||||||
![](../docs/images/cone-2.png)
|
![](../images/cone-2.png)
|
||||||
|
|
||||||
First of all, we have to try to build a frustum cone:
|
First of all, we have to try to build a frustum cone:
|
||||||
```kotlin
|
```kotlin
|
||||||
@ -283,7 +283,7 @@ cone(60, 80, name = "cone") {
|
|||||||
color(0u, 40u, 0u)
|
color(0u, 40u, 0u)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
![](../docs/images/frustum-cone.png)
|
![](../images/frustum-cone.png)
|
||||||
|
|
||||||
Now, we need to make a try to build a cone segment:
|
Now, we need to make a try to build a cone segment:
|
||||||
|
|
||||||
@ -292,8 +292,8 @@ cone(60, 80, angle = PI, name = "cone") {
|
|||||||
color(0u, 0u, 200u)
|
color(0u, 0u, 200u)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
![](../docs/images/cone-segment-1.png)
|
![](../images/cone-segment-1.png)
|
||||||
![](../docs/images/cone-segment-2.png)
|
![](../images/cone-segment-2.png)
|
||||||
|
|
||||||
Finally, the segment of frustum cone is left for a try:
|
Finally, the segment of frustum cone is left for a try:
|
||||||
```kotlin
|
```kotlin
|
||||||
@ -301,7 +301,7 @@ cone(60, 100, 20, PI*3/4, angle = PI/3, name = "cone") {
|
|||||||
color(190u, 0u, 0u)
|
color(190u, 0u, 0u)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
![](../docs/images/frustum-cone-segment.png)
|
![](../images/frustum-cone-segment.png)
|
||||||
|
|
||||||
### 4) Cone Surface
|
### 4) Cone Surface
|
||||||
This solid is set by seven values:`bottomOuterRadius`, `bottomInnerRadius`, `height`, `topOuterRadius`, `topInnerRadius`, `startAngle`, and `angle`.
|
This solid is set by seven values:`bottomOuterRadius`, `bottomInnerRadius`, `height`, `topOuterRadius`, `topInnerRadius`, `startAngle`, and `angle`.
|
||||||
@ -318,8 +318,8 @@ Let's build usual cone surface with almost all properties set:
|
|||||||
rotation = Point3D(2, 50, -9)
|
rotation = Point3D(2, 50, -9)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
![](../docs/images/cone-surface-1.png)
|
![](../images/cone-surface-1.png)
|
||||||
![](../docs/images/cone-surface-2.png)
|
![](../images/cone-surface-2.png)
|
||||||
|
|
||||||
Now, let's create a cone surface and set all it's properties:
|
Now, let's create a cone surface and set all it's properties:
|
||||||
|
|
||||||
@ -329,8 +329,8 @@ coneSurface(30, 25, 10, 10, 8,0f, pi*3/4, name = "cone surface") {
|
|||||||
rotation = Point3D(2, 50, -9)
|
rotation = Point3D(2, 50, -9)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
![](../docs/images/cone-surface-fragment.png)
|
![](../images/cone-surface-fragment.png)
|
||||||
![](../docs/images/cone-surface-fragment-2.png)
|
![](../images/cone-surface-fragment-2.png)
|
||||||
|
|
||||||
### 5) Cylinder
|
### 5) Cylinder
|
||||||
|
|
||||||
@ -344,8 +344,8 @@ cylinder(40, 100, "cylinder"){
|
|||||||
color("indigo")
|
color("indigo")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
![](../docs/images/cylinder-1.png)
|
![](../images/cylinder-1.png)
|
||||||
![](../docs/images/cylinder-2.png)
|
![](../images/cylinder-2.png)
|
||||||
### 6) Tube
|
### 6) Tube
|
||||||
|
|
||||||
`tube` takes in `radius`, `height`, `innerRadius`, `startAngle`, `angle`, and `name`. *All values are familiar from `cone`, and `coneSurface` solids.*
|
`tube` takes in `radius`, `height`, `innerRadius`, `startAngle`, `angle`, and `name`. *All values are familiar from `cone`, and `coneSurface` solids.*
|
||||||
@ -356,7 +356,7 @@ tube(50, 40, 20, name = "usual tube"){
|
|||||||
opacity = 0.4
|
opacity = 0.4
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
![](../docs/images/tube.png)
|
![](../images/tube.png)
|
||||||
|
|
||||||
This is an example of tube fragment:
|
This is an example of tube fragment:
|
||||||
|
|
||||||
@ -365,7 +365,7 @@ tube(50, 40, 20, 0f, PI, name = "fragmented tube"){
|
|||||||
color("white")
|
color("white")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
![](../docs/images/tube-fragment.png)
|
![](../images/tube-fragment.png)
|
||||||
### 7) Extruded
|
### 7) Extruded
|
||||||
|
|
||||||
`extruded` is set by two values: `shape`, and `layer`.
|
`extruded` is set by two values: `shape`, and `layer`.
|
@ -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
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
kotlin.code.style=official
|
kotlin.code.style=official
|
||||||
kotlin.mpp.stability.nowarn=true
|
kotlin.mpp.stability.nowarn=true
|
||||||
|
|
||||||
kotlin.jupyter.add.scanner=false
|
kotlin.jupyter.add.scanner=false
|
||||||
|
#kotlin.incremental.js.ir=true
|
||||||
|
|
||||||
org.gradle.jvmargs=-XX:MaxMetaspaceSize=1G
|
|
||||||
org.gradle.parallel=true
|
org.gradle.parallel=true
|
||||||
|
org.gradle.jvmargs=-Xmx4G
|
||||||
|
|
||||||
publishing.github=false
|
toolsVersion=0.12.0-kotlin-1.7.20-Beta
|
||||||
publishing.sonatype=false
|
|
||||||
|
|
||||||
toolsVersion=0.11.1-kotlin-1.6.10
|
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,5 +1,5 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id("ru.mipt.npm.gradle.mpp")
|
id("space.kscience.gradle.mpp")
|
||||||
id("org.jetbrains.kotlin.jupyter.api")
|
id("org.jetbrains.kotlin.jupyter.api")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -21,5 +21,5 @@ kotlin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
readme {
|
readme {
|
||||||
maturity = ru.mipt.npm.gradle.Maturity.EXPERIMENTAL
|
maturity = space.kscience.gradle.Maturity.EXPERIMENTAL
|
||||||
}
|
}
|
@ -1,5 +1,5 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id("ru.mipt.npm.gradle.mpp")
|
id("space.kscience.gradle.mpp")
|
||||||
}
|
}
|
||||||
|
|
||||||
description = "Jupyter api artifact for GDML rendering"
|
description = "Jupyter api artifact for GDML rendering"
|
||||||
@ -56,5 +56,5 @@ kscience {
|
|||||||
}
|
}
|
||||||
|
|
||||||
readme {
|
readme {
|
||||||
maturity = ru.mipt.npm.gradle.Maturity.EXPERIMENTAL
|
maturity = space.kscience.gradle.Maturity.EXPERIMENTAL
|
||||||
}
|
}
|
File diff suppressed because it is too large
Load Diff
@ -12,13 +12,14 @@ pluginManagement {
|
|||||||
maven("https://repo.kotlin.link")
|
maven("https://repo.kotlin.link")
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
gradlePluginPortal()
|
gradlePluginPortal()
|
||||||
|
maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
|
||||||
}
|
}
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("ru.mipt.npm.gradle.project") version toolsVersion
|
id("space.kscience.gradle.project") version toolsVersion
|
||||||
id("ru.mipt.npm.gradle.mpp") version toolsVersion
|
id("space.kscience.gradle.mpp") version toolsVersion
|
||||||
id("ru.mipt.npm.gradle.jvm") version toolsVersion
|
id("space.kscience.gradle.jvm") version toolsVersion
|
||||||
id("ru.mipt.npm.gradle.js") version toolsVersion
|
id("space.kscience.gradle.js") version toolsVersion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,7 +35,7 @@ dependencyResolutionManagement {
|
|||||||
|
|
||||||
versionCatalogs {
|
versionCatalogs {
|
||||||
create("npmlibs") {
|
create("npmlibs") {
|
||||||
from("ru.mipt.npm:version-catalog:$toolsVersion")
|
from("space.kscience:version-catalog:$toolsVersion")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
plugins {
|
plugins {
|
||||||
kotlin("js")
|
kotlin("js")
|
||||||
id("ru.mipt.npm.gradle.js")
|
id("space.kscience.gradle.js")
|
||||||
}
|
}
|
||||||
|
|
||||||
val dataforgeVersion: String by rootProject.extra
|
val dataforgeVersion: String by rootProject.extra
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package space.kscience.visionforge.bootstrap
|
package space.kscience.visionforge.bootstrap
|
||||||
|
|
||||||
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.css.BorderStyle
|
import kotlinx.css.BorderStyle
|
||||||
import kotlinx.css.Color
|
import kotlinx.css.Color
|
||||||
import kotlinx.css.padding
|
import kotlinx.css.padding
|
||||||
@ -15,7 +17,6 @@ import react.RBuilder
|
|||||||
import react.dom.attrs
|
import react.dom.attrs
|
||||||
import react.dom.button
|
import react.dom.button
|
||||||
import react.fc
|
import react.fc
|
||||||
import space.kscience.dataforge.meta.withDefault
|
|
||||||
import space.kscience.visionforge.Vision
|
import space.kscience.visionforge.Vision
|
||||||
import space.kscience.visionforge.encodeToString
|
import space.kscience.visionforge.encodeToString
|
||||||
import space.kscience.visionforge.react.flexColumn
|
import space.kscience.visionforge.react.flexColumn
|
||||||
@ -47,6 +48,7 @@ public external interface CanvasControlsProps : Props {
|
|||||||
public var vision: Vision?
|
public var vision: Vision?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public val CanvasControls: FC<CanvasControlsProps> = fc("CanvasControls") { props ->
|
public val CanvasControls: FC<CanvasControlsProps> = fc("CanvasControls") { props ->
|
||||||
flexColumn {
|
flexColumn {
|
||||||
flexRow {
|
flexRow {
|
||||||
@ -68,9 +70,10 @@ public val CanvasControls: FC<CanvasControlsProps> = fc("CanvasControls") { prop
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@OptIn(DelicateCoroutinesApi::class)
|
||||||
propertyEditor(
|
propertyEditor(
|
||||||
ownProperties = props.canvasOptions.meta,
|
scope = props.vision?.manager?.context ?: GlobalScope,
|
||||||
allProperties = props.canvasOptions.meta.withDefault(Canvas3DOptions.descriptor.defaultNode),
|
properties = props.canvasOptions.meta,
|
||||||
descriptor = Canvas3DOptions.descriptor,
|
descriptor = Canvas3DOptions.descriptor,
|
||||||
expanded = false
|
expanded = false
|
||||||
)
|
)
|
||||||
|
@ -30,7 +30,7 @@ public external interface TabPaneProps : PropsWithChildren {
|
|||||||
public val TabPane: FC<TabPaneProps> = fc("TabPane") { props ->
|
public val TabPane: FC<TabPaneProps> = fc("TabPane") { props ->
|
||||||
var activeTab: String? by useState(props.activeTab)
|
var activeTab: String? by useState(props.activeTab)
|
||||||
|
|
||||||
val children: Array<out ReactElement?> = Children.map(props.children) {
|
val children: Array<out ReactElement<*>?> = Children.map(props.children) {
|
||||||
it.asElementOrNull()
|
it.asElementOrNull()
|
||||||
} ?: emptyArray()
|
} ?: emptyArray()
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -2,13 +2,17 @@ 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 react.key
|
||||||
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||||
|
import space.kscience.dataforge.meta.isEmpty
|
||||||
import space.kscience.visionforge.Vision
|
import space.kscience.visionforge.Vision
|
||||||
import space.kscience.visionforge.computeProperties
|
|
||||||
import space.kscience.visionforge.getStyle
|
import space.kscience.visionforge.getStyle
|
||||||
|
import space.kscience.visionforge.react.EditorPropertyState
|
||||||
|
import space.kscience.visionforge.react.PropertyEditor
|
||||||
import space.kscience.visionforge.react.metaViewer
|
import space.kscience.visionforge.react.metaViewer
|
||||||
import space.kscience.visionforge.react.propertyEditor
|
import space.kscience.visionforge.react.render
|
||||||
|
import space.kscience.visionforge.root
|
||||||
import space.kscience.visionforge.solid.SolidReference
|
import space.kscience.visionforge.solid.SolidReference
|
||||||
import space.kscience.visionforge.styles
|
import space.kscience.visionforge.styles
|
||||||
|
|
||||||
@ -19,12 +23,26 @@ public fun RBuilder.visionPropertyEditor(
|
|||||||
) {
|
) {
|
||||||
|
|
||||||
card("Properties") {
|
card("Properties") {
|
||||||
propertyEditor(
|
child(PropertyEditor) {
|
||||||
ownProperties = vision.meta,
|
attrs {
|
||||||
allProperties = vision.computeProperties(),
|
this.key = key?.toString()
|
||||||
descriptor = descriptor,
|
this.meta = vision.properties.root()
|
||||||
key = key
|
this.updates = vision.properties.changes
|
||||||
)
|
this.descriptor = descriptor
|
||||||
|
this.scope = vision.manager?.context ?: error("Orphan vision could not be observed")
|
||||||
|
this.getPropertyState = { name ->
|
||||||
|
val ownMeta = vision.properties.own?.getMeta(name)
|
||||||
|
if (ownMeta != null && !ownMeta.isEmpty()) {
|
||||||
|
EditorPropertyState.Defined
|
||||||
|
} else if (vision.properties.root().getValue(name) != null) {
|
||||||
|
// TODO differentiate
|
||||||
|
EditorPropertyState.Default()
|
||||||
|
} else {
|
||||||
|
EditorPropertyState.Undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val styles = if (vision is SolidReference) {
|
val styles = if (vision is SolidReference) {
|
||||||
(vision.styles + vision.prototype.styles).distinct()
|
(vision.styles + vision.prototype.styles).distinct()
|
||||||
@ -50,6 +68,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)
|
||||||
}
|
}
|
@ -1,5 +1,5 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id("ru.mipt.npm.gradle.js")
|
id("space.kscience.gradle.js")
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies{
|
dependencies{
|
||||||
|
@ -10,22 +10,22 @@ import react.dom.attrs
|
|||||||
import react.dom.option
|
import react.dom.option
|
||||||
import react.dom.select
|
import react.dom.select
|
||||||
import react.fc
|
import react.fc
|
||||||
|
import space.kscience.dataforge.meta.asValue
|
||||||
import space.kscience.dataforge.meta.descriptors.allowedValues
|
import space.kscience.dataforge.meta.descriptors.allowedValues
|
||||||
import space.kscience.dataforge.values.asValue
|
import space.kscience.dataforge.meta.string
|
||||||
import space.kscience.dataforge.values.string
|
|
||||||
|
|
||||||
@JsExport
|
@JsExport
|
||||||
public val MultiSelectChooser: FC<ValueChooserProps> = fc("MultiSelectChooser") { props ->
|
public val MultiSelectChooser: FC<ValueChooserProps> = fc("MultiSelectChooser") { props ->
|
||||||
val onChange: (Event) -> Unit = { event: Event ->
|
val onChange: (Event) -> Unit = { event: Event ->
|
||||||
val newSelected = (event.target as HTMLSelectElement).selectedOptions.asList()
|
val newSelected = (event.target as HTMLSelectElement).selectedOptions.asList()
|
||||||
.map { (it as HTMLOptionElement).value.asValue() }
|
.map { (it as HTMLOptionElement).value.asValue() }
|
||||||
props.meta.value = newSelected.asValue()
|
props.onValueChange(newSelected.asValue())
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
attrs {
|
attrs {
|
||||||
multiple = true
|
multiple = true
|
||||||
values = (props.actual.value?.list ?: emptyList()).mapTo(HashSet()) { it.string }
|
values = (props.value?.list ?: emptyList()).mapTo(HashSet()) { it.string }
|
||||||
onChangeFunction = onChange
|
onChangeFunction = onChange
|
||||||
}
|
}
|
||||||
props.descriptor?.allowedValues?.forEach { optionValue ->
|
props.descriptor?.allowedValues?.forEach { optionValue ->
|
||||||
|
@ -1,13 +1,18 @@
|
|||||||
package space.kscience.visionforge.react
|
package space.kscience.visionforge.react
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.callbackFlow
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.css.*
|
import kotlinx.css.*
|
||||||
import kotlinx.css.properties.TextDecoration
|
import kotlinx.css.properties.TextDecoration
|
||||||
import kotlinx.html.js.onClickFunction
|
import kotlinx.html.js.onClickFunction
|
||||||
import org.w3c.dom.Element
|
|
||||||
import org.w3c.dom.events.Event
|
import org.w3c.dom.events.Event
|
||||||
import react.*
|
import react.*
|
||||||
import react.dom.attrs
|
import react.dom.attrs
|
||||||
import react.dom.render
|
|
||||||
import space.kscience.dataforge.meta.*
|
import space.kscience.dataforge.meta.*
|
||||||
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||||
import space.kscience.dataforge.meta.descriptors.ValueRequirement
|
import space.kscience.dataforge.meta.descriptors.ValueRequirement
|
||||||
@ -19,17 +24,30 @@ import styled.styledButton
|
|||||||
import styled.styledDiv
|
import styled.styledDiv
|
||||||
import styled.styledSpan
|
import styled.styledSpan
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The display state of a property
|
||||||
|
*/
|
||||||
|
public sealed class EditorPropertyState {
|
||||||
|
public object Defined : EditorPropertyState()
|
||||||
|
public class Default(public val source: String = "unknown") : EditorPropertyState()
|
||||||
|
|
||||||
|
public object Undefined : EditorPropertyState()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public external interface PropertyEditorProps : Props {
|
public external interface PropertyEditorProps : Props {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Root config object - always non-null
|
* Root config object - always non-null
|
||||||
*/
|
*/
|
||||||
public var meta: ObservableMutableMeta
|
public var meta: MutableMeta
|
||||||
|
|
||||||
/**
|
public var getPropertyState: (Name) -> EditorPropertyState
|
||||||
* Provide default item (greyed out if used)
|
|
||||||
*/
|
public var scope: CoroutineScope
|
||||||
public var withDefault: MetaProvider
|
|
||||||
|
public var updates: Flow<Name>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Full path to the displayed node in [meta]. Could be empty
|
* Full path to the displayed node in [meta]. Could be empty
|
||||||
@ -54,7 +72,9 @@ private val PropertyEditorItem: FC<PropertyEditorProps> = fc("PropertyEditorItem
|
|||||||
private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) {
|
private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) {
|
||||||
var expanded: Boolean by useState { props.expanded ?: true }
|
var expanded: Boolean by useState { props.expanded ?: true }
|
||||||
val descriptor: MetaDescriptor? = useMemo(props.descriptor, props.name) { props.descriptor?.get(props.name) }
|
val descriptor: MetaDescriptor? = useMemo(props.descriptor, props.name) { props.descriptor?.get(props.name) }
|
||||||
var ownProperty: ObservableMutableMeta by useState { props.meta.getOrCreate(props.name) }
|
var property: MutableMeta by useState { props.meta.getOrCreate(props.name) }
|
||||||
|
var editorPropertyState: EditorPropertyState by useState { props.getPropertyState(props.name) }
|
||||||
|
|
||||||
|
|
||||||
val keys = useMemo(descriptor) {
|
val keys = useMemo(descriptor) {
|
||||||
buildSet {
|
buildSet {
|
||||||
@ -70,17 +90,19 @@ private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) {
|
|||||||
val token = props.name.lastOrNull()?.toString() ?: "Properties"
|
val token = props.name.lastOrNull()?.toString() ?: "Properties"
|
||||||
|
|
||||||
fun update() {
|
fun update() {
|
||||||
ownProperty = props.meta.getOrCreate(props.name)
|
property = props.meta.getOrCreate(props.name)
|
||||||
|
editorPropertyState = props.getPropertyState(props.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(props.meta) {
|
useEffect(props.meta) {
|
||||||
props.meta.onChange(props) { updatedName ->
|
val job = props.updates.onEach { updatedName ->
|
||||||
if (updatedName == props.name) {
|
if (updatedName == props.name) {
|
||||||
update()
|
update()
|
||||||
}
|
}
|
||||||
}
|
}.launchIn(props.scope)
|
||||||
|
|
||||||
cleanup {
|
cleanup {
|
||||||
props.meta.removeListener(props)
|
job.cancel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,7 +137,7 @@ private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) {
|
|||||||
styledSpan {
|
styledSpan {
|
||||||
css {
|
css {
|
||||||
+TreeStyles.treeLabel
|
+TreeStyles.treeLabel
|
||||||
if (ownProperty.isEmpty()) {
|
if (editorPropertyState != EditorPropertyState.Defined) {
|
||||||
+TreeStyles.treeLabelInactive
|
+TreeStyles.treeLabelInactive
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -131,8 +153,12 @@ private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) {
|
|||||||
ValueChooser {
|
ValueChooser {
|
||||||
attrs {
|
attrs {
|
||||||
this.descriptor = descriptor
|
this.descriptor = descriptor
|
||||||
this.meta = ownProperty
|
this.state = editorPropertyState
|
||||||
this.actual = props.withDefault.getMeta(props.name) ?: ownProperty
|
this.value = property.value
|
||||||
|
this.onValueChange = {
|
||||||
|
property.value = it
|
||||||
|
editorPropertyState = props.getPropertyState(props.name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -156,7 +182,7 @@ private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) {
|
|||||||
}
|
}
|
||||||
+"\u00D7"
|
+"\u00D7"
|
||||||
attrs {
|
attrs {
|
||||||
if (ownProperty.isEmpty()) {
|
if (editorPropertyState!= EditorPropertyState.Defined) {
|
||||||
disabled = true
|
disabled = true
|
||||||
} else {
|
} else {
|
||||||
onClickFunction = removeClick
|
onClickFunction = removeClick
|
||||||
@ -179,9 +205,11 @@ private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) {
|
|||||||
attrs {
|
attrs {
|
||||||
this.key = props.name.toString()
|
this.key = props.name.toString()
|
||||||
this.meta = props.meta
|
this.meta = props.meta
|
||||||
this.withDefault = props.withDefault
|
|
||||||
this.name = props.name + token
|
this.name = props.name + token
|
||||||
this.descriptor = props.descriptor
|
this.descriptor = props.descriptor
|
||||||
|
this.scope = props.scope
|
||||||
|
this.getPropertyState = { props.getPropertyState(props.name + token) }
|
||||||
|
this.updates = props.updates
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//configEditor(props.root, props.name + token, props.descriptor, props.default)
|
//configEditor(props.root, props.name + token, props.descriptor, props.default)
|
||||||
@ -197,44 +225,51 @@ public val PropertyEditor: FC<PropertyEditorProps> = fc("PropertyEditor") { prop
|
|||||||
attrs {
|
attrs {
|
||||||
this.key = ""
|
this.key = ""
|
||||||
this.meta = props.meta
|
this.meta = props.meta
|
||||||
this.withDefault = props.withDefault
|
|
||||||
this.name = Name.EMPTY
|
this.name = Name.EMPTY
|
||||||
this.descriptor = props.descriptor
|
this.descriptor = props.descriptor
|
||||||
this.expanded = props.expanded
|
this.expanded = props.expanded
|
||||||
|
this.scope = props.scope
|
||||||
|
this.getPropertyState = props.getPropertyState
|
||||||
|
this.updates = props.updates
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
public fun RBuilder.propertyEditor(
|
public fun RBuilder.propertyEditor(
|
||||||
ownProperties: ObservableMutableMeta,
|
scope: CoroutineScope,
|
||||||
allProperties: MetaProvider = ownProperties,
|
properties: ObservableMutableMeta,
|
||||||
descriptor: MetaDescriptor? = null,
|
descriptor: MetaDescriptor? = null,
|
||||||
key: Any? = null,
|
key: Any? = null,
|
||||||
expanded: Boolean? = null,
|
expanded: Boolean? = null,
|
||||||
) {
|
) {
|
||||||
child(PropertyEditor) {
|
child(PropertyEditor) {
|
||||||
attrs {
|
attrs {
|
||||||
this.meta = ownProperties
|
this.meta = properties
|
||||||
this.withDefault = allProperties
|
|
||||||
this.descriptor = descriptor
|
this.descriptor = descriptor
|
||||||
this.key = key?.toString() ?: ""
|
this.key = key?.toString() ?: ""
|
||||||
this.expanded = expanded
|
this.expanded = expanded
|
||||||
|
this.scope = scope
|
||||||
|
this.getPropertyState = { name ->
|
||||||
|
if (properties[name] != null) {
|
||||||
|
EditorPropertyState.Defined
|
||||||
|
} else if (descriptor?.get(name)?.defaultValue != null) {
|
||||||
|
EditorPropertyState.Default("descriptor")
|
||||||
|
} else {
|
||||||
|
EditorPropertyState.Undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.updates = callbackFlow {
|
||||||
|
properties.onChange(scope) { name ->
|
||||||
|
scope.launch {
|
||||||
|
send(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
invokeOnClose {
|
||||||
|
properties.removeListener(scope)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public fun RBuilder.configEditor(
|
|
||||||
config: ObservableMutableMeta,
|
|
||||||
default: MetaProvider = config,
|
|
||||||
descriptor: MetaDescriptor? = null,
|
|
||||||
key: Any? = null,
|
|
||||||
): Unit = propertyEditor(config, default, descriptor, key = key)
|
|
||||||
|
|
||||||
public fun Element.configEditor(
|
|
||||||
config: ObservableMutableMeta,
|
|
||||||
default: Meta = config,
|
|
||||||
descriptor: MetaDescriptor? = null,
|
|
||||||
key: Any? = null,
|
|
||||||
): Unit = render(this) {
|
|
||||||
configEditor(config, default, descriptor, key = key)
|
|
||||||
}
|
}
|
@ -10,32 +10,34 @@ import react.FC
|
|||||||
import react.dom.attrs
|
import react.dom.attrs
|
||||||
import react.fc
|
import react.fc
|
||||||
import react.useState
|
import react.useState
|
||||||
|
import space.kscience.dataforge.meta.asValue
|
||||||
import space.kscience.dataforge.meta.descriptors.ValueRequirement
|
import space.kscience.dataforge.meta.descriptors.ValueRequirement
|
||||||
import space.kscience.dataforge.meta.double
|
import space.kscience.dataforge.meta.double
|
||||||
import space.kscience.dataforge.meta.get
|
import space.kscience.dataforge.meta.get
|
||||||
import space.kscience.dataforge.meta.string
|
import space.kscience.dataforge.meta.string
|
||||||
import space.kscience.dataforge.values.asValue
|
|
||||||
import styled.css
|
import styled.css
|
||||||
import styled.styledInput
|
import styled.styledInput
|
||||||
|
|
||||||
@JsExport
|
@JsExport
|
||||||
public val RangeValueChooser: FC<ValueChooserProps> = fc("RangeValueChooser") { props ->
|
public val RangeValueChooser: FC<ValueChooserProps> = fc("RangeValueChooser") { props ->
|
||||||
var innerValue by useState(props.actual.double)
|
var innerValue by useState(props.value?.double)
|
||||||
var rangeDisabled: Boolean by useState(props.meta.value == null)
|
var rangeDisabled: Boolean by useState(props.state != EditorPropertyState.Defined)
|
||||||
|
|
||||||
val handleDisable: (Event) -> Unit = {
|
val handleDisable: (Event) -> Unit = {
|
||||||
val checkBoxValue = (it.target as HTMLInputElement).checked
|
val checkBoxValue = (it.target as HTMLInputElement).checked
|
||||||
rangeDisabled = !checkBoxValue
|
rangeDisabled = !checkBoxValue
|
||||||
props.meta.value = if (!checkBoxValue) {
|
props.onValueChange(
|
||||||
null
|
if (!checkBoxValue) {
|
||||||
} else {
|
null
|
||||||
innerValue?.asValue()
|
} else {
|
||||||
}
|
innerValue?.asValue()
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val handleChange: (Event) -> Unit = {
|
val handleChange: (Event) -> Unit = {
|
||||||
val newValue = (it.target as HTMLInputElement).value
|
val newValue = (it.target as HTMLInputElement).value
|
||||||
props.meta.value = newValue.toDoubleOrNull()?.asValue()
|
props.onValueChange(newValue.toDoubleOrNull()?.asValue())
|
||||||
innerValue = newValue.toDoubleOrNull()
|
innerValue = newValue.toDoubleOrNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ import space.kscience.dataforge.names.plus
|
|||||||
import space.kscience.dataforge.names.startsWith
|
import space.kscience.dataforge.names.startsWith
|
||||||
import space.kscience.visionforge.Vision
|
import space.kscience.visionforge.Vision
|
||||||
import space.kscience.visionforge.VisionGroup
|
import space.kscience.visionforge.VisionGroup
|
||||||
|
import space.kscience.visionforge.asSequence
|
||||||
import space.kscience.visionforge.isEmpty
|
import space.kscience.visionforge.isEmpty
|
||||||
import styled.css
|
import styled.css
|
||||||
import styled.styledDiv
|
import styled.styledDiv
|
||||||
@ -61,7 +62,7 @@ private fun RBuilder.visionTree(props: ObjectTreeProps): Unit {
|
|||||||
//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.children.keys.any { !it.body.startsWith("@") }) {
|
||||||
styledSpan {
|
styledSpan {
|
||||||
css {
|
css {
|
||||||
+TreeStyles.treeCaret
|
+TreeStyles.treeCaret
|
||||||
@ -81,9 +82,9 @@ private fun RBuilder.visionTree(props: ObjectTreeProps): Unit {
|
|||||||
css {
|
css {
|
||||||
+TreeStyles.tree
|
+TreeStyles.tree
|
||||||
}
|
}
|
||||||
obj.children.entries
|
obj.children.asSequence()
|
||||||
.filter { !it.key.toString().startsWith("@") } // ignore statics and other hidden children
|
.filter { !it.first.toString().startsWith("@") } // ignore statics and other hidden children
|
||||||
.sortedBy { (it.value as? VisionGroup)?.isEmpty() ?: true } // ignore empty groups
|
.sortedBy { (it.second as? VisionGroup)?.children?.isEmpty() ?: true } // ignore empty groups
|
||||||
.forEach { (childToken, child) ->
|
.forEach { (childToken, child) ->
|
||||||
styledDiv {
|
styledDiv {
|
||||||
css {
|
css {
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
package space.kscience.visionforge.react
|
||||||
|
|
||||||
|
import react.Props
|
||||||
|
import react.RBuilder
|
||||||
|
import react.createElement
|
||||||
|
import react.dom.client.Root
|
||||||
|
|
||||||
|
public fun Root.render(block: RBuilder.() -> Unit) {
|
||||||
|
render(createElement<Props>(block))
|
||||||
|
}
|
@ -19,29 +19,27 @@ import react.useState
|
|||||||
import space.kscience.dataforge.meta.*
|
import space.kscience.dataforge.meta.*
|
||||||
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||||
import space.kscience.dataforge.meta.descriptors.allowedValues
|
import space.kscience.dataforge.meta.descriptors.allowedValues
|
||||||
import space.kscience.dataforge.values.ValueType
|
|
||||||
import space.kscience.dataforge.values.asValue
|
|
||||||
import space.kscience.dataforge.values.int
|
|
||||||
import space.kscience.dataforge.values.string
|
|
||||||
import space.kscience.visionforge.Colors
|
import space.kscience.visionforge.Colors
|
||||||
import space.kscience.visionforge.widgetType
|
import space.kscience.visionforge.widgetType
|
||||||
import styled.css
|
import styled.css
|
||||||
import styled.styledInput
|
import styled.styledInput
|
||||||
import styled.styledSelect
|
import styled.styledSelect
|
||||||
|
import three.math.Color
|
||||||
|
|
||||||
public external interface ValueChooserProps : Props {
|
public external interface ValueChooserProps : Props {
|
||||||
public var descriptor: MetaDescriptor?
|
public var descriptor: MetaDescriptor?
|
||||||
public var meta: ObservableMutableMeta
|
public var state: EditorPropertyState
|
||||||
public var actual: Meta
|
public var value: Value?
|
||||||
|
public var onValueChange: (Value?) -> Unit
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsExport
|
@JsExport
|
||||||
public val StringValueChooser: FC<ValueChooserProps> = fc("StringValueChooser") { props ->
|
public val StringValueChooser: FC<ValueChooserProps> = fc("StringValueChooser") { props ->
|
||||||
var value by useState(props.actual.string ?: "")
|
var value by useState(props.value?.string ?: "")
|
||||||
val keyDown: (Event) -> Unit = { event ->
|
val keyDown: (Event) -> Unit = { event ->
|
||||||
if (event.type == "keydown" && event.asDynamic().key == "Enter") {
|
if (event.type == "keydown" && event.asDynamic().key == "Enter") {
|
||||||
value = (event.target as HTMLInputElement).value
|
value = (event.target as HTMLInputElement).value
|
||||||
props.meta.value = value.asValue()
|
props.onValueChange(value.asValue())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val handleChange: (Event) -> Unit = {
|
val handleChange: (Event) -> Unit = {
|
||||||
@ -63,7 +61,7 @@ public val StringValueChooser: FC<ValueChooserProps> = fc("StringValueChooser")
|
|||||||
public val BooleanValueChooser: FC<ValueChooserProps> = fc("BooleanValueChooser") { props ->
|
public val BooleanValueChooser: FC<ValueChooserProps> = fc("BooleanValueChooser") { props ->
|
||||||
val handleChange: (Event) -> Unit = {
|
val handleChange: (Event) -> Unit = {
|
||||||
val newValue = (it.target as HTMLInputElement).checked
|
val newValue = (it.target as HTMLInputElement).checked
|
||||||
props.meta.value = newValue.asValue()
|
props.onValueChange(newValue.asValue())
|
||||||
}
|
}
|
||||||
styledInput(type = InputType.checkBox) {
|
styledInput(type = InputType.checkBox) {
|
||||||
css {
|
css {
|
||||||
@ -71,7 +69,7 @@ public val BooleanValueChooser: FC<ValueChooserProps> = fc("BooleanValueChooser"
|
|||||||
}
|
}
|
||||||
attrs {
|
attrs {
|
||||||
//this.attributes["indeterminate"] = (props.item == null).toString()
|
//this.attributes["indeterminate"] = (props.item == null).toString()
|
||||||
checked = props.actual.boolean ?: false
|
checked = props.value?.boolean ?: false
|
||||||
onChangeFunction = handleChange
|
onChangeFunction = handleChange
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -79,7 +77,7 @@ public val BooleanValueChooser: FC<ValueChooserProps> = fc("BooleanValueChooser"
|
|||||||
|
|
||||||
@JsExport
|
@JsExport
|
||||||
public val NumberValueChooser: FC<ValueChooserProps> = fc("NumberValueChooser") { props ->
|
public val NumberValueChooser: FC<ValueChooserProps> = fc("NumberValueChooser") { props ->
|
||||||
var innerValue by useState(props.actual.string ?: "")
|
var innerValue by useState(props.value?.string ?: "")
|
||||||
val keyDown: (Event) -> Unit = { event ->
|
val keyDown: (Event) -> Unit = { event ->
|
||||||
if (event.type == "keydown" && event.asDynamic().key == "Enter") {
|
if (event.type == "keydown" && event.asDynamic().key == "Enter") {
|
||||||
innerValue = (event.target as HTMLInputElement).value
|
innerValue = (event.target as HTMLInputElement).value
|
||||||
@ -87,7 +85,7 @@ public val NumberValueChooser: FC<ValueChooserProps> = fc("NumberValueChooser")
|
|||||||
if (number == null) {
|
if (number == null) {
|
||||||
console.error("The input value $innerValue is not a number")
|
console.error("The input value $innerValue is not a number")
|
||||||
} else {
|
} else {
|
||||||
props.meta.value = number.asValue()
|
props.onValueChange(number.asValue())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -117,10 +115,10 @@ public val NumberValueChooser: FC<ValueChooserProps> = fc("NumberValueChooser")
|
|||||||
|
|
||||||
@JsExport
|
@JsExport
|
||||||
public val ComboValueChooser: FC<ValueChooserProps> = fc("ComboValueChooser") { props ->
|
public val ComboValueChooser: FC<ValueChooserProps> = fc("ComboValueChooser") { props ->
|
||||||
var selected by useState(props.actual.string ?: "")
|
var selected by useState(props.value?.string ?: "")
|
||||||
val handleChange: (Event) -> Unit = {
|
val handleChange: (Event) -> Unit = {
|
||||||
selected = (it.target as HTMLSelectElement).value
|
selected = (it.target as HTMLSelectElement).value
|
||||||
props.meta.value = selected.asValue()
|
props.onValueChange(selected.asValue())
|
||||||
}
|
}
|
||||||
styledSelect {
|
styledSelect {
|
||||||
css {
|
css {
|
||||||
@ -132,7 +130,7 @@ public val ComboValueChooser: FC<ValueChooserProps> = fc("ComboValueChooser") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
attrs {
|
attrs {
|
||||||
this.value = props.actual.string ?: ""
|
this.value = props.value?.string ?: ""
|
||||||
multiple = false
|
multiple = false
|
||||||
onChangeFunction = handleChange
|
onChangeFunction = handleChange
|
||||||
}
|
}
|
||||||
@ -142,7 +140,7 @@ public val ComboValueChooser: FC<ValueChooserProps> = fc("ComboValueChooser") {
|
|||||||
@JsExport
|
@JsExport
|
||||||
public val ColorValueChooser: FC<ValueChooserProps> = fc("ColorValueChooser") { props ->
|
public val ColorValueChooser: FC<ValueChooserProps> = fc("ColorValueChooser") { props ->
|
||||||
val handleChange: (Event) -> Unit = {
|
val handleChange: (Event) -> Unit = {
|
||||||
props.meta.value = (it.target as HTMLInputElement).value.asValue()
|
props.onValueChange((it.target as HTMLInputElement).value.asValue())
|
||||||
}
|
}
|
||||||
styledInput(type = InputType.color) {
|
styledInput(type = InputType.color) {
|
||||||
css {
|
css {
|
||||||
@ -150,9 +148,9 @@ public val ColorValueChooser: FC<ValueChooserProps> = fc("ColorValueChooser") {
|
|||||||
margin(0.px)
|
margin(0.px)
|
||||||
}
|
}
|
||||||
attrs {
|
attrs {
|
||||||
this.value = props.actual.value?.let { value ->
|
this.value = props.value?.let { value ->
|
||||||
if (value.type == ValueType.NUMBER) Colors.rgbToString(value.int)
|
if (value.type == ValueType.NUMBER) Colors.rgbToString(value.int)
|
||||||
else value.string
|
else "#" + Color(value.string).getHexString()
|
||||||
} ?: "#000000"
|
} ?: "#000000"
|
||||||
onChangeFunction = handleChange
|
onChangeFunction = handleChange
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id("ru.mipt.npm.gradle.js")
|
id("space.kscience.gradle.js")
|
||||||
}
|
}
|
||||||
|
|
||||||
val dataforgeVersion: String by rootProject.extra
|
val dataforgeVersion: String by rootProject.extra
|
||||||
|
|
||||||
kotlin{
|
kotlin{
|
||||||
js{
|
js(IR){
|
||||||
useCommonJs()
|
useCommonJs()
|
||||||
browser {
|
browser {
|
||||||
commonWebpackConfig {
|
commonWebpackConfig {
|
||||||
|
@ -5,39 +5,46 @@ import kotlinx.coroutines.async
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.css.*
|
import kotlinx.css.*
|
||||||
import react.*
|
import react.*
|
||||||
|
import react.dom.b
|
||||||
import react.dom.div
|
import react.dom.div
|
||||||
|
import react.dom.p
|
||||||
import react.dom.span
|
import react.dom.span
|
||||||
import ringui.*
|
import ringui.*
|
||||||
import space.kscience.dataforge.context.Context
|
import space.kscience.dataforge.meta.get
|
||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.Name
|
||||||
import space.kscience.dataforge.names.NameToken
|
import space.kscience.dataforge.names.NameToken
|
||||||
import space.kscience.dataforge.names.isEmpty
|
import space.kscience.dataforge.names.isEmpty
|
||||||
import space.kscience.dataforge.names.length
|
import space.kscience.dataforge.names.length
|
||||||
import space.kscience.visionforge.*
|
import space.kscience.visionforge.*
|
||||||
import space.kscience.visionforge.react.ThreeCanvasComponent
|
import space.kscience.visionforge.react.*
|
||||||
import space.kscience.visionforge.react.flexColumn
|
|
||||||
import space.kscience.visionforge.react.flexRow
|
|
||||||
import space.kscience.visionforge.react.propertyEditor
|
|
||||||
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.Solids
|
||||||
|
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
|
||||||
|
|
||||||
public external interface ThreeCanvasWithControlsProps : Props {
|
public external interface ThreeCanvasWithControlsProps : Props {
|
||||||
public var context: Context
|
public var solids: Solids
|
||||||
public var builderOfSolid: Deferred<Solid?>
|
public var builderOfSolid: Deferred<Solid?>
|
||||||
public var selected: Name?
|
public var selected: Name?
|
||||||
public var options: Canvas3DOptions?
|
public var options: Canvas3DOptions?
|
||||||
public var additionalTabs: Map<String, RBuilder.() -> Unit>?
|
public var additionalTabs: Map<String, RBuilder.() -> Unit>?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val ThreeCanvasWithControlsProps.context get() = solids.context
|
||||||
|
|
||||||
public fun ThreeCanvasWithControlsProps.solid(block: SolidGroup.() -> Unit) {
|
public fun ThreeCanvasWithControlsProps.solid(block: SolidGroup.() -> Unit) {
|
||||||
builderOfSolid = context.async {
|
builderOfSolid = context.async {
|
||||||
SolidGroup(block)
|
solids.solidGroup(null, block)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public fun ThreeCanvasWithControlsProps.options(block: Canvas3DOptions.() -> Unit) {
|
||||||
|
options = Canvas3DOptions(block)
|
||||||
|
}
|
||||||
|
|
||||||
public fun ThreeCanvasWithControlsProps.tab(title: String, block: RBuilder.() -> Unit) {
|
public fun ThreeCanvasWithControlsProps.tab(title: String, block: RBuilder.() -> Unit) {
|
||||||
additionalTabs = (additionalTabs ?: emptyMap()) + (title to block)
|
additionalTabs = (additionalTabs ?: emptyMap()) + (title to block)
|
||||||
}
|
}
|
||||||
@ -77,14 +84,14 @@ public fun RBuilder.nameCrumbs(name: Name?, link: (Name) -> Unit): Unit = styled
|
|||||||
|
|
||||||
@JsExport
|
@JsExport
|
||||||
public val ThreeCanvasWithControls: FC<ThreeCanvasWithControlsProps> = fc("ThreeViewWithControls") { props ->
|
public val ThreeCanvasWithControls: FC<ThreeCanvasWithControlsProps> = fc("ThreeViewWithControls") { props ->
|
||||||
var selected by useState { props.selected }
|
var selected: Name? by useState { props.selected }
|
||||||
var solid: Solid? by useState(null)
|
var solid: Solid? by useState(null)
|
||||||
|
|
||||||
useEffect {
|
useEffect {
|
||||||
props.context.launch {
|
props.context.launch {
|
||||||
solid = props.builderOfSolid.await()
|
solid = props.builderOfSolid.await()
|
||||||
//ensure that the solid is properly rooted
|
//ensure that the solid is properly rooted
|
||||||
if(solid?.parent == null){
|
if (solid?.parent == null) {
|
||||||
solid?.setAsRoot(props.context.visionManager)
|
solid?.setAsRoot(props.context.visionManager)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -104,7 +111,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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -160,12 +167,31 @@ public val ThreeCanvasWithControls: FC<ThreeCanvasWithControlsProps> = fc("Three
|
|||||||
nameCrumbs(selected) { selected = it }
|
nameCrumbs(selected) { selected = it }
|
||||||
}
|
}
|
||||||
IslandContent {
|
IslandContent {
|
||||||
propertyEditor(
|
child(PropertyEditor) {
|
||||||
ownProperties = vision.meta,
|
attrs {
|
||||||
allProperties = vision.computeProperties(),
|
this.key = selected.toString()
|
||||||
descriptor = vision.descriptor,
|
this.meta = vision.properties.root()
|
||||||
key = selected
|
this.updates = vision.properties.changes
|
||||||
)
|
this.descriptor = vision.descriptor
|
||||||
|
this.scope = props.context
|
||||||
|
this.getPropertyState = { name ->
|
||||||
|
if (vision.properties.own?.get(name) != null) {
|
||||||
|
EditorPropertyState.Defined
|
||||||
|
} else if (vision.properties.root()[name] != null) {
|
||||||
|
// TODO differentiate
|
||||||
|
EditorPropertyState.Default()
|
||||||
|
} else {
|
||||||
|
EditorPropertyState.Undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vision.styles.takeIf { it.isNotEmpty() }?.let { styles ->
|
||||||
|
p {
|
||||||
|
b { +"Styles: " }
|
||||||
|
+styles.joinToString(separator = ", ")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ package space.kscience.visionforge.ring
|
|||||||
|
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import org.w3c.dom.Element
|
import org.w3c.dom.Element
|
||||||
import react.child
|
import react.dom.client.createRoot
|
||||||
import space.kscience.dataforge.context.AbstractPlugin
|
import space.kscience.dataforge.context.AbstractPlugin
|
||||||
import space.kscience.dataforge.context.Context
|
import space.kscience.dataforge.context.Context
|
||||||
import space.kscience.dataforge.context.PluginFactory
|
import space.kscience.dataforge.context.PluginFactory
|
||||||
@ -12,6 +12,7 @@ import space.kscience.dataforge.names.Name
|
|||||||
import space.kscience.dataforge.names.asName
|
import space.kscience.dataforge.names.asName
|
||||||
import space.kscience.visionforge.ElementVisionRenderer
|
import space.kscience.visionforge.ElementVisionRenderer
|
||||||
import space.kscience.visionforge.Vision
|
import space.kscience.visionforge.Vision
|
||||||
|
import space.kscience.visionforge.react.render
|
||||||
import space.kscience.visionforge.solid.Solid
|
import space.kscience.visionforge.solid.Solid
|
||||||
import space.kscience.visionforge.solid.three.ThreePlugin
|
import space.kscience.visionforge.solid.three.ThreePlugin
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
@ -25,10 +26,10 @@ public class ThreeWithControlsPlugin : AbstractPlugin(), ElementVisionRenderer {
|
|||||||
if (vision is Solid) ElementVisionRenderer.DEFAULT_RATING * 2 else ElementVisionRenderer.ZERO_RATING
|
if (vision is Solid) ElementVisionRenderer.DEFAULT_RATING * 2 else ElementVisionRenderer.ZERO_RATING
|
||||||
|
|
||||||
override fun render(element: Element, vision: Vision, meta: Meta) {
|
override fun render(element: Element, vision: Vision, meta: Meta) {
|
||||||
react.dom.render(element) {
|
createRoot(element).render {
|
||||||
child(ThreeCanvasWithControls) {
|
child(ThreeCanvasWithControls) {
|
||||||
attrs {
|
attrs {
|
||||||
this.context = this@ThreeWithControlsPlugin.context
|
this.solids = three.solids
|
||||||
this.builderOfSolid = context.async { vision as Solid}
|
this.builderOfSolid = context.async { vision as Solid}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -45,6 +46,7 @@ public class ThreeWithControlsPlugin : AbstractPlugin(), ElementVisionRenderer {
|
|||||||
public companion object : PluginFactory<ThreeWithControlsPlugin> {
|
public companion object : PluginFactory<ThreeWithControlsPlugin> {
|
||||||
override val tag: PluginTag = PluginTag("vision.threejs.withControls", PluginTag.DATAFORGE_GROUP)
|
override val tag: PluginTag = PluginTag("vision.threejs.withControls", PluginTag.DATAFORGE_GROUP)
|
||||||
override val type: KClass<ThreeWithControlsPlugin> = ThreeWithControlsPlugin::class
|
override val type: KClass<ThreeWithControlsPlugin> = ThreeWithControlsPlugin::class
|
||||||
override fun invoke(meta: Meta, context: Context): ThreeWithControlsPlugin = ThreeWithControlsPlugin()
|
|
||||||
|
override fun build(context: Context, meta: Meta): ThreeWithControlsPlugin = ThreeWithControlsPlugin()
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,18 +2,18 @@ package space.kscience.visionforge.ring
|
|||||||
|
|
||||||
import org.w3c.dom.Element
|
import org.w3c.dom.Element
|
||||||
import react.RBuilder
|
import react.RBuilder
|
||||||
|
import react.dom.client.createRoot
|
||||||
import react.dom.p
|
import react.dom.p
|
||||||
import react.dom.render
|
import react.key
|
||||||
import ringui.Island
|
import ringui.Island
|
||||||
import ringui.SmartTabs
|
import ringui.SmartTabs
|
||||||
import ringui.Tab
|
import ringui.Tab
|
||||||
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||||
|
import space.kscience.dataforge.meta.get
|
||||||
import space.kscience.visionforge.Vision
|
import space.kscience.visionforge.Vision
|
||||||
import space.kscience.visionforge.computeProperties
|
|
||||||
import space.kscience.visionforge.getStyle
|
import space.kscience.visionforge.getStyle
|
||||||
import space.kscience.visionforge.react.flexColumn
|
import space.kscience.visionforge.react.*
|
||||||
import space.kscience.visionforge.react.metaViewer
|
import space.kscience.visionforge.root
|
||||||
import space.kscience.visionforge.react.propertyEditor
|
|
||||||
import space.kscience.visionforge.solid.SolidReference
|
import space.kscience.visionforge.solid.SolidReference
|
||||||
import space.kscience.visionforge.styles
|
import space.kscience.visionforge.styles
|
||||||
|
|
||||||
@ -30,12 +30,25 @@ public fun RBuilder.ringPropertyEditor(
|
|||||||
|
|
||||||
flexColumn {
|
flexColumn {
|
||||||
Island("Properties") {
|
Island("Properties") {
|
||||||
propertyEditor(
|
child(PropertyEditor) {
|
||||||
ownProperties = vision.meta,
|
attrs {
|
||||||
allProperties = vision.computeProperties(),
|
this.key = key?.toString()
|
||||||
descriptor = descriptor,
|
this.meta = vision.properties.root()
|
||||||
key = key
|
this.updates = vision.properties.changes
|
||||||
)
|
this.descriptor = descriptor
|
||||||
|
this.scope = vision.manager?.context ?: error("Orphan vision could not be observed")
|
||||||
|
this.getPropertyState = {name->
|
||||||
|
if(vision.properties.own?.get(name)!= null){
|
||||||
|
EditorPropertyState.Defined
|
||||||
|
} else if(vision.properties.root()[name] != null){
|
||||||
|
// TODO differentiate
|
||||||
|
EditorPropertyState.Default()
|
||||||
|
} else {
|
||||||
|
EditorPropertyState.Undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (styles.isNotEmpty()) {
|
if (styles.isNotEmpty()) {
|
||||||
@ -72,6 +85,6 @@ public fun RBuilder.ringPropertyEditor(
|
|||||||
public fun Element.ringPropertyEditor(
|
public fun Element.ringPropertyEditor(
|
||||||
item: Vision,
|
item: Vision,
|
||||||
descriptor: MetaDescriptor? = item.descriptor,
|
descriptor: MetaDescriptor? = item.descriptor,
|
||||||
): Unit = render(this) {
|
): Unit = createRoot(this).render {
|
||||||
ringPropertyEditor(item, descriptor = descriptor)
|
ringPropertyEditor(item, descriptor = descriptor)
|
||||||
}
|
}
|
@ -1,5 +1,7 @@
|
|||||||
package space.kscience.visionforge.ring
|
package space.kscience.visionforge.ring
|
||||||
|
|
||||||
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.css.BorderStyle
|
import kotlinx.css.BorderStyle
|
||||||
import kotlinx.css.Color
|
import kotlinx.css.Color
|
||||||
import kotlinx.css.padding
|
import kotlinx.css.padding
|
||||||
@ -18,7 +20,6 @@ import react.fc
|
|||||||
import ringui.Island
|
import ringui.Island
|
||||||
import ringui.SmartTabs
|
import ringui.SmartTabs
|
||||||
import ringui.Tab
|
import ringui.Tab
|
||||||
import space.kscience.dataforge.meta.withDefault
|
|
||||||
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.encodeToString
|
import space.kscience.visionforge.encodeToString
|
||||||
@ -52,6 +53,7 @@ internal external interface CanvasControlsProps : Props {
|
|||||||
public var vision: Vision?
|
public var vision: Vision?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(DelicateCoroutinesApi::class)
|
||||||
internal val CanvasControls: FC<CanvasControlsProps> = fc("CanvasControls") { props ->
|
internal val CanvasControls: FC<CanvasControlsProps> = fc("CanvasControls") { props ->
|
||||||
flexColumn {
|
flexColumn {
|
||||||
flexRow {
|
flexRow {
|
||||||
@ -75,8 +77,8 @@ internal val CanvasControls: FC<CanvasControlsProps> = fc("CanvasControls") { pr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
propertyEditor(
|
propertyEditor(
|
||||||
ownProperties = props.options.meta,
|
scope = props.vision?.manager?.context ?: GlobalScope,
|
||||||
allProperties = props.options.meta.withDefault(Canvas3DOptions.descriptor.defaultNode),
|
properties = props.options.meta,
|
||||||
descriptor = Canvas3DOptions.descriptor,
|
descriptor = Canvas3DOptions.descriptor,
|
||||||
expanded = false
|
expanded = false
|
||||||
)
|
)
|
||||||
|
@ -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 {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id("ru.mipt.npm.gradle.mpp")
|
id("space.kscience.gradle.mpp")
|
||||||
}
|
}
|
||||||
|
|
||||||
val dataforgeVersion: String by rootProject.extra
|
val dataforgeVersion: String by rootProject.extra
|
||||||
@ -13,6 +13,11 @@ kotlin {
|
|||||||
api("org.jetbrains.kotlin-wrappers:kotlin-css")
|
api("org.jetbrains.kotlin-wrappers:kotlin-css")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
commonTest{
|
||||||
|
dependencies{
|
||||||
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:${space.kscience.gradle.KScienceVersions.coroutinesVersion}")
|
||||||
|
}
|
||||||
|
}
|
||||||
jsMain {
|
jsMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
api("org.jetbrains.kotlin-wrappers:kotlin-extensions")
|
api("org.jetbrains.kotlin-wrappers:kotlin-extensions")
|
||||||
@ -28,5 +33,5 @@ kscience{
|
|||||||
}
|
}
|
||||||
|
|
||||||
readme{
|
readme{
|
||||||
maturity = ru.mipt.npm.gradle.Maturity.DEVELOPMENT
|
maturity = space.kscience.gradle.Maturity.DEVELOPMENT
|
||||||
}
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package space.kscience.visionforge
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.Transient
|
||||||
|
import space.kscience.dataforge.meta.*
|
||||||
|
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||||
|
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
public abstract class AbstractVision : Vision {
|
||||||
|
|
||||||
|
@Transient
|
||||||
|
override var parent: Vision? = null
|
||||||
|
|
||||||
|
@SerialName("properties")
|
||||||
|
protected var propertiesInternal: MutableMeta? = null
|
||||||
|
|
||||||
|
final override val properties: MutableVisionProperties by lazy {
|
||||||
|
object : AbstractVisionProperties(this) {
|
||||||
|
override var properties: MutableMeta?
|
||||||
|
get() = propertiesInternal
|
||||||
|
set(value) {
|
||||||
|
propertiesInternal = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override val descriptor: MetaDescriptor? get() = null
|
||||||
|
}
|
@ -1,11 +1,6 @@
|
|||||||
package space.kscience.visionforge
|
package space.kscience.visionforge
|
||||||
|
|
||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.*
|
||||||
import space.kscience.dataforge.meta.get
|
|
||||||
import space.kscience.dataforge.meta.number
|
|
||||||
import space.kscience.dataforge.values.ValueType
|
|
||||||
import space.kscience.dataforge.values.int
|
|
||||||
import space.kscience.dataforge.values.string
|
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
||||||
@ -18,14 +18,14 @@ private tailrec fun styleIsDefined(vision: Vision, reference: StyleReference): B
|
|||||||
}
|
}
|
||||||
|
|
||||||
@VisionBuilder
|
@VisionBuilder
|
||||||
public fun Vision.useStyle(reference: StyleReference) {
|
public fun Vision.useStyle(reference: StyleReference, notify: Boolean = true) {
|
||||||
//check that style is defined in a parent
|
//check that style is defined in a parent
|
||||||
//check(styleIsDefined(this, reference)) { "Style reference does not belong to a Vision parent" }
|
//check(styleIsDefined(this, reference)) { "Style reference does not belong to a Vision parent" }
|
||||||
useStyle(reference.name)
|
useStyle(reference.name, notify)
|
||||||
}
|
}
|
||||||
|
|
||||||
@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,
|
||||||
|
@ -5,20 +5,17 @@ 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.stringList
|
|
||||||
import kotlin.jvm.JvmInline
|
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.properties.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 +23,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.properties.setProperty(STYLESHEET_KEY + key, style)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -43,7 +40,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())
|
||||||
}
|
}
|
||||||
@ -59,49 +56,48 @@ internal fun Vision.styleChanged(key: String, oldStyle: Meta?, newStyle: Meta?)
|
|||||||
val tokens: Collection<Name> =
|
val tokens: Collection<Name> =
|
||||||
((oldStyle?.items?.keys ?: emptySet()) + (newStyle?.items?.keys ?: emptySet()))
|
((oldStyle?.items?.keys ?: emptySet()) + (newStyle?.items?.keys ?: emptySet()))
|
||||||
.map { it.asName() }
|
.map { it.asName() }
|
||||||
tokens.forEach { parent?.invalidateProperty(it) }
|
tokens.forEach { parent?.properties?.invalidate(it) }
|
||||||
}
|
}
|
||||||
if (this is VisionGroup) {
|
children?.forEach { _, vision ->
|
||||||
for (obj in this) {
|
vision.styleChanged(key, oldStyle, newStyle)
|
||||||
obj.styleChanged(key, oldStyle, newStyle)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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() = properties.getValue(Vision.STYLE_KEY, inherit = false, includeStyles = false)?.stringList ?: emptyList()
|
||||||
set(value) {
|
set(value) {
|
||||||
meta.setValue(Vision.STYLE_KEY, value.map { it.asValue() }.asValue())
|
properties.setValue(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, notify: Boolean = true) {
|
||||||
styles = (meta.getMeta(Vision.STYLE_KEY)?.stringList ?: emptyList()) + name
|
val newStyle = properties.own?.get(Vision.STYLE_KEY)?.value?.list?.plus(name.asValue()) ?: listOf(name.asValue())
|
||||||
|
properties.setValue(Vision.STYLE_KEY, newStyle.asValue(), notify)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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)
|
properties.own?.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
|
||||||
|
@ -1,147 +1,73 @@
|
|||||||
package space.kscience.visionforge
|
package space.kscience.visionforge
|
||||||
|
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.channels.awaitClose
|
import kotlinx.coroutines.Job
|
||||||
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.meta.asValue
|
||||||
import space.kscience.dataforge.meta.*
|
import space.kscience.dataforge.meta.boolean
|
||||||
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.misc.Type
|
import space.kscience.dataforge.misc.Type
|
||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.Name
|
||||||
import space.kscience.dataforge.names.asName
|
import space.kscience.dataforge.names.asName
|
||||||
import space.kscience.dataforge.names.startsWith
|
import space.kscience.visionforge.AbstractVisionGroup.Companion.updateProperties
|
||||||
import space.kscience.dataforge.values.Value
|
|
||||||
import space.kscience.dataforge.values.asValue
|
|
||||||
import space.kscience.dataforge.values.boolean
|
|
||||||
import space.kscience.visionforge.Vision.Companion.TYPE
|
import space.kscience.visionforge.Vision.Companion.TYPE
|
||||||
import kotlin.reflect.KProperty1
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A root type for display hierarchy
|
* A root type for display hierarchy
|
||||||
*/
|
*/
|
||||||
@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
|
||||||
|
|
||||||
/**
|
|
||||||
* This Vision own properties (ignoring inheritance, styles and defaults)
|
|
||||||
*/
|
|
||||||
override val meta: ObservableMutableMeta
|
|
||||||
|
|
||||||
/**
|
public val properties: MutableVisionProperties
|
||||||
* Get property value with given layer flags.
|
|
||||||
* @param inherit toggles parent node property lookup. Null means inference from descriptor. Default is false.
|
|
||||||
* @param includeStyles toggles inclusion of properties from styles. default is true
|
|
||||||
*/
|
|
||||||
public fun getPropertyValue(
|
|
||||||
name: Name,
|
|
||||||
inherit: Boolean = false,
|
|
||||||
includeStyles: Boolean = true,
|
|
||||||
includeDefaults: Boolean = true,
|
|
||||||
): Value?
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Notify all listeners that a property has been changed and should be invalidated
|
|
||||||
*/
|
|
||||||
public fun invalidateProperty(propertyName: Name)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update this vision using a dif represented by [VisionChange].
|
* Update this vision using a dif represented by [VisionChange].
|
||||||
*/
|
*/
|
||||||
public fun update(change: VisionChange)
|
public fun update(change: VisionChange) {
|
||||||
|
change.properties?.let {
|
||||||
|
updateProperties(it, Name.EMPTY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override val descriptor: MetaDescriptor?
|
override val descriptor: MetaDescriptor?
|
||||||
|
|
||||||
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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flow of property invalidation events. It does not contain property values after invalidation since it is not clear
|
* Control visibility of the element
|
||||||
* if it should include inherited properties etc.
|
|
||||||
*/
|
*/
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
public var Vision.visible: Boolean?
|
||||||
@DFExperimental
|
get() = properties.getValue(Vision.VISIBLE_KEY)?.boolean
|
||||||
public val Vision.propertyChanges: Flow<Name>
|
set(value) {
|
||||||
get() = callbackFlow {
|
properties.setValue(Vision.VISIBLE_KEY, value?.asValue())
|
||||||
meta.onChange(this) { name ->
|
|
||||||
launch {
|
|
||||||
send(name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
awaitClose {
|
|
||||||
meta.removeListener(this)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subscribe on property updates. The subscription is bound to the given scope and canceled when the scope is canceled
|
* Subscribe on property updates. The subscription is bound to the given scope and canceled when the scope is canceled
|
||||||
*/
|
*/
|
||||||
public fun Vision.onPropertyChange(callback: Meta.(Name) -> Unit) {
|
public fun Vision.onPropertyChange(
|
||||||
meta.onChange(null, callback)
|
scope: CoroutineScope? = manager?.context,
|
||||||
}
|
callback: (Name) -> Unit
|
||||||
|
): Job = properties.changes.onEach {
|
||||||
/**
|
callback(it)
|
||||||
* Get [Vision] property using key as a String
|
}.launchIn(scope ?: error("Orphan Vision can't observe properties"))
|
||||||
*/
|
|
||||||
public fun Vision.getPropertyValue(
|
|
||||||
key: String,
|
|
||||||
inherit: Boolean = false,
|
|
||||||
includeStyles: Boolean = true,
|
|
||||||
includeDefaults: Boolean = true,
|
|
||||||
): Value? = getPropertyValue(Name.parse(key), inherit, includeStyles, includeDefaults)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A convenience method to set property node or value. If Item is null, then node is removed, not a value
|
|
||||||
*/
|
|
||||||
public fun Vision.setProperty(name: Name, item: Any?) {
|
|
||||||
when (item) {
|
|
||||||
null -> meta.remove(name)
|
|
||||||
is Meta -> meta.setMeta(name, item)
|
|
||||||
is Value -> meta.setValue(name, item)
|
|
||||||
else -> meta.setValue(name, Value.of(item))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun Vision.setPropertyNode(key: String, item: Any?) {
|
|
||||||
setProperty(Name.parse(key), item)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Control visibility of the element
|
|
||||||
*/
|
|
||||||
public var Vision.visible: Boolean?
|
|
||||||
get() = getPropertyValue(Vision.VISIBLE_KEY)?.boolean
|
|
||||||
set(value) = meta.setValue(Vision.VISIBLE_KEY, value?.asValue())
|
|
||||||
|
|
||||||
|
|
||||||
public fun <V : Vision, T> V.useProperty(
|
|
||||||
property: KProperty1<V, T>,
|
|
||||||
owner: Any? = null,
|
|
||||||
callBack: V.(T) -> Unit,
|
|
||||||
) {
|
|
||||||
//Pass initial value.
|
|
||||||
callBack(property.get(this))
|
|
||||||
meta.onChange(owner) { name ->
|
|
||||||
if (name.startsWith(property.name.asName())) {
|
|
||||||
callBack(property.get(this@useProperty))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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)
|
|
||||||
//}
|
|
@ -7,54 +7,75 @@ 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.meta.descriptors.MetaDescriptor
|
||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.Name
|
||||||
|
import space.kscience.dataforge.names.isEmpty
|
||||||
import space.kscience.dataforge.names.plus
|
import space.kscience.dataforge.names.plus
|
||||||
import space.kscience.dataforge.values.Null
|
|
||||||
import kotlin.jvm.Synchronized
|
import kotlin.jvm.Synchronized
|
||||||
import kotlin.time.Duration
|
import kotlin.time.Duration
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a deep copy of given Vision without external connections.
|
* Create a deep copy of given Vision without external connections.
|
||||||
*/
|
*/
|
||||||
private fun Vision.deepCopy(): Vision {
|
private fun Vision.deepCopy(manager: VisionManager): Vision {
|
||||||
|
if (this is NullVision) return NullVision
|
||||||
|
|
||||||
//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]
|
* A vision used only in change propagation and showing that the target should be removed
|
||||||
*/
|
*/
|
||||||
public class VisionChangeBuilder : VisionContainerBuilder<Vision> {
|
@Serializable
|
||||||
|
public object NullVision : Vision {
|
||||||
|
override var parent: Vision?
|
||||||
|
get() = null
|
||||||
|
set(_) {
|
||||||
|
error("Can't set parent for null vision")
|
||||||
|
}
|
||||||
|
|
||||||
|
override val properties: MutableVisionProperties get() = error("Can't get properties of `NullVision`")
|
||||||
|
|
||||||
|
override val descriptor: MetaDescriptor? = null
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An update for a [Vision]
|
||||||
|
*/
|
||||||
|
public class VisionChangeBuilder(private val manager: VisionManager) : MutableVisionContainer<Vision> {
|
||||||
|
|
||||||
private var reset: Boolean = false
|
|
||||||
private var vision: Vision? = null
|
private var vision: Vision? = null
|
||||||
private val propertyChange = MutableMeta()
|
private var propertyChange = MutableMeta()
|
||||||
private val children: HashMap<Name, VisionChangeBuilder> = HashMap()
|
private val children: HashMap<Name, VisionChangeBuilder> = HashMap()
|
||||||
|
|
||||||
public fun isEmpty(): Boolean = propertyChange.isEmpty() && propertyChange.isEmpty() && children.isEmpty()
|
public fun isEmpty(): Boolean = propertyChange.isEmpty() && propertyChange.isEmpty() && children.isEmpty()
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
private fun getOrPutChild(visionName: Name): VisionChangeBuilder =
|
private fun getOrPutChild(visionName: Name): VisionChangeBuilder =
|
||||||
children.getOrPut(visionName) { VisionChangeBuilder() }
|
children.getOrPut(visionName) { VisionChangeBuilder(manager) }
|
||||||
|
|
||||||
public fun propertyChanged(visionName: Name, propertyName: Name, item: Meta?) {
|
public fun propertyChanged(visionName: Name, propertyName: Name, item: Meta?) {
|
||||||
if (visionName == Name.EMPTY) {
|
if (visionName == Name.EMPTY) {
|
||||||
//Write property removal as [Null]
|
//Write property removal as [Null]
|
||||||
propertyChange[propertyName] = (item ?: Meta(Null))
|
if (propertyName.isEmpty()) {
|
||||||
|
propertyChange = item?.toMutableMeta() ?: MutableMeta()
|
||||||
|
} else {
|
||||||
|
propertyChange[propertyName] = (item ?: Meta(Null))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
getOrPutChild(visionName).propertyChanged(Name.EMPTY, propertyName, item)
|
getOrPutChild(visionName).propertyChanged(Name.EMPTY, propertyName, item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun set(name: Name?, child: Vision?) {
|
override fun setChild(name: Name?, child: Vision?) {
|
||||||
if (name == null) error("Static children are not allowed in VisionChange")
|
if (name == null) error("Static children are not allowed in VisionChange")
|
||||||
getOrPutChild(name).apply {
|
getOrPutChild(name).apply {
|
||||||
vision = child
|
vision = child ?: NullVision
|
||||||
reset = vision == null
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,32 +83,28 @@ public class VisionChangeBuilder : VisionContainerBuilder<Vision> {
|
|||||||
* Isolate collected changes by creating detached copies of given visions
|
* Isolate collected changes by creating detached copies of given visions
|
||||||
*/
|
*/
|
||||||
public fun deepCopy(): VisionChange = VisionChange(
|
public fun deepCopy(): VisionChange = VisionChange(
|
||||||
reset,
|
vision?.deepCopy(manager),
|
||||||
vision?.deepCopy(),
|
|
||||||
if (propertyChange.isEmpty()) null else propertyChange.seal(),
|
if (propertyChange.isEmpty()) null else propertyChange.seal(),
|
||||||
if (children.isEmpty()) null else children.mapValues { it.value.deepCopy() }
|
if (children.isEmpty()) null else children.mapValues { it.value.deepCopy() }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param delete flag showing that this vision child should be removed
|
* @param vision a new value for vision content. If the Vision is to be removed should be [NullVision]
|
||||||
* @param vision a new value for vision content
|
|
||||||
* @param properties updated properties
|
* @param properties updated properties
|
||||||
* @param children a map of children changed in ths [VisionChange]. If a child to be removed, set [delete] flag to true.
|
* @param children a map of children changed in ths [VisionChange]. If a child to be removed, set [delete] flag to true.
|
||||||
*/
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
public data class VisionChange(
|
public data class VisionChange(
|
||||||
public val delete: Boolean = false,
|
|
||||||
public val vision: Vision? = null,
|
public val vision: Vision? = null,
|
||||||
@Serializable(MetaSerializer::class) public val properties: Meta? = null,
|
public val properties: Meta? = null,
|
||||||
public val children: Map<Name, VisionChange>? = null,
|
public val children: Map<Name, VisionChange>? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
public inline fun VisionChange(block: VisionChangeBuilder.() -> Unit): VisionChange =
|
public inline fun VisionManager.VisionChange(block: VisionChangeBuilder.() -> Unit): VisionChange =
|
||||||
VisionChangeBuilder().apply(block).deepCopy()
|
VisionChangeBuilder(this).apply(block).deepCopy()
|
||||||
|
|
||||||
|
|
||||||
@OptIn(DFExperimental::class)
|
|
||||||
private fun CoroutineScope.collectChange(
|
private fun CoroutineScope.collectChange(
|
||||||
name: Name,
|
name: Name,
|
||||||
source: Vision,
|
source: Vision,
|
||||||
@ -95,29 +112,26 @@ private fun CoroutineScope.collectChange(
|
|||||||
) {
|
) {
|
||||||
|
|
||||||
//Collect properties change
|
//Collect properties change
|
||||||
source.onPropertyChange { propertyName ->
|
source.onPropertyChange(this) { propertyName ->
|
||||||
val newItem = source.meta[propertyName]
|
val newItem = source.properties.own?.get(propertyName)
|
||||||
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) ->
|
children?.forEach { token, child ->
|
||||||
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().setChild(fullName, after)
|
||||||
|
}?.launchIn(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -126,13 +140,14 @@ private fun CoroutineScope.collectChange(
|
|||||||
public fun Vision.flowChanges(
|
public fun Vision.flowChanges(
|
||||||
collectionDuration: Duration,
|
collectionDuration: Duration,
|
||||||
): Flow<VisionChange> = flow {
|
): Flow<VisionChange> = flow {
|
||||||
|
val manager = manager ?: error("Orphan vision could not collect changes")
|
||||||
|
|
||||||
var collector = VisionChangeBuilder()
|
var collector = VisionChangeBuilder(manager)
|
||||||
coroutineScope {
|
coroutineScope {
|
||||||
collectChange(Name.EMPTY, this@flowChanges) { collector }
|
collectChange(Name.EMPTY, this@flowChanges) { collector }
|
||||||
|
|
||||||
//Send initial vision state
|
//Send initial vision state
|
||||||
val initialChange = VisionChange(vision = deepCopy())
|
val initialChange = VisionChange(vision = deepCopy(manager))
|
||||||
emit(initialChange)
|
emit(initialChange)
|
||||||
|
|
||||||
while (currentCoroutineContext().isActive) {
|
while (currentCoroutineContext().isActive) {
|
||||||
@ -143,7 +158,7 @@ public fun Vision.flowChanges(
|
|||||||
//emit changes
|
//emit changes
|
||||||
emit(collector.deepCopy())
|
emit(collector.deepCopy())
|
||||||
//Reset the collector
|
//Reset the collector
|
||||||
collector = VisionChangeBuilder()
|
collector = VisionChangeBuilder(manager)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,217 @@
|
|||||||
|
package space.kscience.visionforge
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.flow.*
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import space.kscience.dataforge.names.*
|
||||||
|
import space.kscience.visionforge.VisionChildren.Companion.STATIC_TOKEN_BODY
|
||||||
|
import kotlin.jvm.Synchronized
|
||||||
|
|
||||||
|
@DslMarker
|
||||||
|
public annotation class VisionBuilder
|
||||||
|
|
||||||
|
public interface VisionContainer<out V : Vision> {
|
||||||
|
public fun getChild(name: Name): V?
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface MutableVisionContainer<in V : Vision> {
|
||||||
|
//TODO add documentation
|
||||||
|
public fun setChild(name: Name?, child: V?)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A serializable representation of [Vision] children container
|
||||||
|
*/
|
||||||
|
public interface VisionChildren : VisionContainer<Vision> {
|
||||||
|
public val group: 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 getChild(name: Name): Vision? = when (name.length) {
|
||||||
|
0 -> group
|
||||||
|
1 -> get(name.first())
|
||||||
|
else -> get(name.first())?.children?.getChild(name.cutFirst())
|
||||||
|
}
|
||||||
|
|
||||||
|
public companion object {
|
||||||
|
public const val STATIC_TOKEN_BODY: String = "@static"
|
||||||
|
|
||||||
|
public fun empty(owner: Vision): VisionChildren = object : VisionChildren {
|
||||||
|
override val group: 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 operator fun VisionChildren.get(name: Name): Vision? = getChild(name)
|
||||||
|
public operator fun VisionChildren.get(name: String): Vision? = getChild(name)
|
||||||
|
|
||||||
|
|
||||||
|
public fun VisionChildren.isEmpty(): Boolean = keys.isEmpty()
|
||||||
|
|
||||||
|
public inline fun VisionChildren.forEach(block: (NameToken, Vision) -> Unit) {
|
||||||
|
keys.forEach { block(it, get(it)!!) }
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface MutableVisionChildren : VisionChildren, MutableVisionContainer<Vision> {
|
||||||
|
|
||||||
|
public override val group: MutableVisionGroup
|
||||||
|
|
||||||
|
public operator fun set(token: NameToken, value: Vision?)
|
||||||
|
|
||||||
|
override fun setChild(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 ?: group.createGroup().also {
|
||||||
|
set(name.first(), it)
|
||||||
|
}
|
||||||
|
parent.children.setChild(name.cutFirst(), child)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
public operator fun MutableVisionChildren.set(name: Name?, vision: Vision?) {
|
||||||
|
setChild(name, vision)
|
||||||
|
}
|
||||||
|
|
||||||
|
public operator fun MutableVisionChildren.set(name: String?, vision: Vision?) {
|
||||||
|
setChild(name, vision)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a static child. Statics could not be found by name, removed or replaced. Changing statics also do not trigger events.
|
||||||
|
*/
|
||||||
|
public fun MutableVisionChildren.static(child: Vision) {
|
||||||
|
set(NameToken(STATIC_TOKEN_BODY, 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 fun <V : Vision> VisionContainer<V>.getChild(str: String): V? = getChild(Name.parse(str))
|
||||||
|
|
||||||
|
public fun <V : Vision> MutableVisionContainer<V>.setChild(
|
||||||
|
str: String?, vision: V?,
|
||||||
|
): Unit = setChild(str?.parseAsName(), vision)
|
||||||
|
|
||||||
|
internal abstract class VisionChildrenImpl(
|
||||||
|
override val group: MutableVisionGroup,
|
||||||
|
) : MutableVisionChildren {
|
||||||
|
|
||||||
|
private val updateJobs = HashMap<NameToken, Job>()
|
||||||
|
|
||||||
|
abstract var items: MutableMap<NameToken, Vision>?
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
private fun buildItems(): MutableMap<NameToken, Vision> {
|
||||||
|
if (items == null) {
|
||||||
|
items = LinkedHashMap()
|
||||||
|
}
|
||||||
|
return items!!
|
||||||
|
}
|
||||||
|
|
||||||
|
private val scope: CoroutineScope? get() = group.manager?.context
|
||||||
|
|
||||||
|
override val keys: Set<NameToken> get() = items?.keys ?: emptySet()
|
||||||
|
|
||||||
|
override fun get(token: NameToken): Vision? = items?.get(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 ?: buildItems())[token] = value
|
||||||
|
//check if parent already exists and is different from the current one
|
||||||
|
if (value.parent != null && value.parent != group) error("Can't reassign parent Vision for $value")
|
||||||
|
//set parent
|
||||||
|
value.parent = group
|
||||||
|
//start update jobs (only if the vision is rooted)
|
||||||
|
scope?.let { scope ->
|
||||||
|
val job = value.children?.changes?.onEach {
|
||||||
|
onChange(token + it)
|
||||||
|
}?.launchIn(scope)
|
||||||
|
if (job != null) {
|
||||||
|
updateJobs[token] = job
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onChange(token.asName())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun clear() {
|
||||||
|
items?.forEach { set(it.key, null) }
|
||||||
|
// if (!items.isNullOrEmpty()) {
|
||||||
|
// updateJobs.values.forEach {
|
||||||
|
// it.cancel()
|
||||||
|
// }
|
||||||
|
// updateJobs.clear()
|
||||||
|
// items?.clear()
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//
|
||||||
|
//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)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//}
|
@ -1,109 +1,119 @@
|
|||||||
package space.kscience.visionforge
|
package space.kscience.visionforge
|
||||||
|
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.serialization.SerialName
|
||||||
import kotlinx.coroutines.channels.awaitClose
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.coroutines.flow.Flow
|
import space.kscience.dataforge.meta.Meta
|
||||||
import kotlinx.coroutines.flow.callbackFlow
|
import space.kscience.dataforge.meta.ValueType
|
||||||
import kotlinx.coroutines.launch
|
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||||
import space.kscience.dataforge.misc.DFExperimental
|
import space.kscience.dataforge.meta.descriptors.value
|
||||||
import space.kscience.dataforge.names.*
|
import space.kscience.dataforge.names.Name
|
||||||
import space.kscience.dataforge.provider.Provider
|
import space.kscience.dataforge.names.NameToken
|
||||||
|
import space.kscience.dataforge.names.parseAsName
|
||||||
|
import space.kscience.dataforge.names.plus
|
||||||
|
import space.kscience.visionforge.Vision.Companion.STYLE_KEY
|
||||||
|
|
||||||
@DslMarker
|
|
||||||
public annotation class VisionBuilder
|
|
||||||
|
|
||||||
public interface VisionContainer<out V : Vision> {
|
public interface VisionGroup : Vision {
|
||||||
public operator fun get(name: Name): V?
|
public val children: VisionChildren
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public interface MutableVisionGroup : VisionGroup {
|
||||||
* Represents a group of [Vision] instances
|
|
||||||
*/
|
|
||||||
public interface VisionGroup : Provider, Vision, VisionContainer<Vision> {
|
|
||||||
/**
|
|
||||||
* A map of top level named children
|
|
||||||
*/
|
|
||||||
public val children: Map<NameToken, Vision>
|
|
||||||
|
|
||||||
override val defaultTarget: String get() = Vision.TYPE
|
override val children: MutableVisionChildren
|
||||||
|
|
||||||
/**
|
public fun createGroup(): MutableVisionGroup
|
||||||
* A map of direct children for specific target
|
|
||||||
* (currently "visual" or "style")
|
|
||||||
*/
|
|
||||||
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"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public val Vision.children: VisionChildren? get() = (this as? VisionGroup)?.children
|
||||||
* 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]
|
* A full base implementation for a [Vision]
|
||||||
*/
|
*/
|
||||||
public interface MutableVisionGroup : VisionGroup, VisionContainerBuilder<Vision> {
|
@Serializable
|
||||||
public fun onStructureChanged(owner: Any?, block: VisionGroup.(Name) -> Unit)
|
public abstract class AbstractVisionGroup : AbstractVision(), MutableVisionGroup {
|
||||||
|
|
||||||
public fun removeStructureListener(owner: Any?)
|
override fun update(change: VisionChange) {
|
||||||
}
|
change.children?.forEach { (name, change) ->
|
||||||
|
when {
|
||||||
|
change.vision == NullVision -> children.setChild(name, null)
|
||||||
/**
|
change.vision != null -> children.setChild(name, change.vision)
|
||||||
* Flow structure changes of this group. Unconsumed changes are discarded
|
else -> children.getChild(name)?.update(change)
|
||||||
*/
|
|
||||||
@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 childrenInternal: MutableMap<NameToken, Vision>? = null
|
||||||
|
|
||||||
public operator fun <V : Vision> VisionContainer<V>.get(str: String): V? = get(Name.parse(str))
|
|
||||||
|
|
||||||
public operator fun <V : Vision> VisionContainerBuilder<V>.set(token: NameToken, child: V?): Unit =
|
init {
|
||||||
set(token.asName(), child)
|
childrenInternal?.forEach { it.value.parent = this }
|
||||||
|
}
|
||||||
|
|
||||||
public operator fun <V : Vision> VisionContainerBuilder<V>.set(key: String?, child: V?): Unit =
|
override val children: MutableVisionChildren by lazy {
|
||||||
set(key?.let(Name::parse), child)
|
object : VisionChildrenImpl(this) {
|
||||||
|
override var items: MutableMap<NameToken, Vision>?
|
||||||
|
get() = this@AbstractVisionGroup.childrenInternal
|
||||||
|
set(value) {
|
||||||
|
this@AbstractVisionGroup.childrenInternal = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public fun MutableVisionGroup.removeAll(): Unit = children.keys.map { it.asName() }.forEach { this[it] = null }
|
abstract override fun createGroup(): AbstractVisionGroup
|
||||||
|
|
||||||
|
public companion object {
|
||||||
|
public val descriptor: MetaDescriptor = MetaDescriptor {
|
||||||
|
value(STYLE_KEY, ValueType.STRING) {
|
||||||
|
multiple = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun Vision.updateProperties(item: Meta, name: Name = Name.EMPTY) {
|
||||||
|
properties.setValue(name, item.value)
|
||||||
|
item.items.forEach { (token, item) ->
|
||||||
|
updateProperties(item, name + token)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple vision group that just holds children. Nothing else.
|
||||||
|
*/
|
||||||
|
@Serializable
|
||||||
|
@SerialName("vision.group")
|
||||||
|
public class SimpleVisionGroup : AbstractVisionGroup(), MutableVisionContainer<Vision> {
|
||||||
|
override fun createGroup(): SimpleVisionGroup = SimpleVisionGroup()
|
||||||
|
|
||||||
|
override fun setChild(name: Name?, child: Vision?) {
|
||||||
|
children.setChild(name, child)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisionBuilder
|
||||||
|
public inline fun MutableVisionContainer<Vision>.group(
|
||||||
|
name: Name? = null,
|
||||||
|
builder: SimpleVisionGroup.() -> Unit = {},
|
||||||
|
): SimpleVisionGroup = SimpleVisionGroup().also { setChild(name, it) }.apply(builder)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define a group with given [name], attach it to this parent and return it.
|
||||||
|
*/
|
||||||
|
@VisionBuilder
|
||||||
|
public inline fun MutableVisionContainer<Vision>.group(
|
||||||
|
name: String,
|
||||||
|
builder: SimpleVisionGroup.() -> Unit = {},
|
||||||
|
): SimpleVisionGroup = group(name.parseAsName(), builder)
|
||||||
|
|
||||||
|
//fun VisualObject.findStyle(styleName: Name): Meta? {
|
||||||
|
// if (this is VisualGroup) {
|
||||||
|
// val style = resolveStyle(styleName)
|
||||||
|
// if (style != null) return style
|
||||||
|
// }
|
||||||
|
// return parent?.findStyle(styleName)
|
||||||
|
//}
|
@ -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)
|
|
||||||
}
|
|
@ -19,19 +19,20 @@ import space.kscience.visionforge.html.VisionOfNumberField
|
|||||||
import space.kscience.visionforge.html.VisionOfTextField
|
import space.kscience.visionforge.html.VisionOfTextField
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
public class VisionManager(meta: Meta) : AbstractPlugin(meta) {
|
public class VisionManager(meta: Meta) : AbstractPlugin(meta), MutableVisionContainer<Vision> {
|
||||||
override val tag: PluginTag get() = Companion.tag
|
override val tag: PluginTag get() = Companion.tag
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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) {
|
||||||
@ -57,19 +58,23 @@ public class VisionManager(meta: Meta) : AbstractPlugin(meta) {
|
|||||||
public fun encodeToMeta(vision: Vision, descriptor: MetaDescriptor? = null): Meta =
|
public fun encodeToMeta(vision: Vision, descriptor: MetaDescriptor? = null): Meta =
|
||||||
encodeToJsonElement(vision).toMeta(descriptor)
|
encodeToJsonElement(vision).toMeta(descriptor)
|
||||||
|
|
||||||
|
override fun setChild(name: Name?, child: Vision?) {
|
||||||
|
child?.setAsRoot(this)
|
||||||
|
}
|
||||||
|
|
||||||
public companion object : PluginFactory<VisionManager> {
|
public companion object : PluginFactory<VisionManager> {
|
||||||
override val tag: PluginTag = PluginTag(name = "vision", group = PluginTag.DATAFORGE_GROUP)
|
override val tag: PluginTag = PluginTag(name = "vision", group = PluginTag.DATAFORGE_GROUP)
|
||||||
override val type: KClass<out VisionManager> = VisionManager::class
|
override val type: KClass<out VisionManager> = VisionManager::class
|
||||||
|
|
||||||
public const val VISION_SERIALIZER_MODULE_TARGET: String = "visionSerializerModule"
|
public const val VISION_SERIALIZER_MODULE_TARGET: String = "visionSerializerModule"
|
||||||
|
|
||||||
override fun invoke(meta: Meta, context: Context): VisionManager = VisionManager(meta)
|
override fun build(context: Context, meta: Meta): VisionManager = VisionManager(meta)
|
||||||
|
|
||||||
private val defaultSerialModule: SerializersModule = SerializersModule {
|
private val defaultSerialModule: SerializersModule = SerializersModule {
|
||||||
polymorphic(Vision::class) {
|
polymorphic(Vision::class) {
|
||||||
default { VisionBase.serializer() }
|
default { SimpleVisionGroup.serializer() }
|
||||||
subclass(VisionBase.serializer())
|
subclass(NullVision.serializer())
|
||||||
subclass(VisionGroupBase.serializer())
|
subclass(SimpleVisionGroup.serializer())
|
||||||
subclass(VisionOfNumberField.serializer())
|
subclass(VisionOfNumberField.serializer())
|
||||||
subclass(VisionOfTextField.serializer())
|
subclass(VisionOfTextField.serializer())
|
||||||
subclass(VisionOfCheckbox.serializer())
|
subclass(VisionOfCheckbox.serializer())
|
||||||
@ -108,4 +113,19 @@ 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) ?: error("VisionManager not defined in Vision")
|
manager?.encodeToString(this) ?: error("Orphan vision could not be encoded")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A root vision attached to [VisionManager]
|
||||||
|
*/
|
||||||
|
public class RootVision(override val manager: VisionManager) : AbstractVisionGroup() {
|
||||||
|
override fun createGroup(): SimpleVisionGroup = SimpleVisionGroup()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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)
|
||||||
|
}
|
@ -0,0 +1,296 @@
|
|||||||
|
package space.kscience.visionforge
|
||||||
|
|
||||||
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
|
import kotlinx.coroutines.GlobalScope
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
import kotlinx.coroutines.flow.SharedFlow
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.serialization.Transient
|
||||||
|
import space.kscience.dataforge.meta.*
|
||||||
|
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||||
|
import space.kscience.dataforge.meta.descriptors.get
|
||||||
|
import space.kscience.dataforge.names.*
|
||||||
|
import kotlin.jvm.Synchronized
|
||||||
|
|
||||||
|
public interface VisionProperties {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Raw Visions own properties without styles, defaults, etc.
|
||||||
|
*/
|
||||||
|
public val own: Meta?
|
||||||
|
|
||||||
|
public val descriptor: MetaDescriptor?
|
||||||
|
|
||||||
|
public fun getValue(
|
||||||
|
name: Name,
|
||||||
|
inherit: Boolean? = null,
|
||||||
|
includeStyles: Boolean? = null,
|
||||||
|
): 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? = null,
|
||||||
|
includeStyles: Boolean? = null,
|
||||||
|
): Meta
|
||||||
|
|
||||||
|
public val changes: Flow<Name>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 invalidate(propertyName: Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface MutableVisionProperties : VisionProperties {
|
||||||
|
|
||||||
|
override fun getProperty(
|
||||||
|
name: Name,
|
||||||
|
inherit: Boolean?,
|
||||||
|
includeStyles: Boolean?,
|
||||||
|
): MutableMeta = VisionPropertiesItem(
|
||||||
|
this,
|
||||||
|
name,
|
||||||
|
inherit,
|
||||||
|
includeStyles,
|
||||||
|
)
|
||||||
|
|
||||||
|
public fun setProperty(
|
||||||
|
name: Name,
|
||||||
|
node: Meta?,
|
||||||
|
notify: Boolean = true,
|
||||||
|
)
|
||||||
|
|
||||||
|
public fun setValue(
|
||||||
|
name: Name,
|
||||||
|
value: Value?,
|
||||||
|
notify: Boolean = true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun MutableVisionProperties.remove(name: Name) {
|
||||||
|
setProperty(name, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun MutableVisionProperties.remove(name: String) {
|
||||||
|
remove(name.parseAsName())
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisionBuilder
|
||||||
|
public operator fun MutableVisionProperties.invoke(block: MutableMeta.() -> Unit) {
|
||||||
|
root(inherit = false, includeStyles = false).apply(block)
|
||||||
|
}
|
||||||
|
|
||||||
|
private class VisionPropertiesItem(
|
||||||
|
val properties: MutableVisionProperties,
|
||||||
|
val nodeName: Name,
|
||||||
|
val inherit: Boolean? = null,
|
||||||
|
val useStyles: Boolean? = null,
|
||||||
|
val default: Meta? = null,
|
||||||
|
) : MutableMeta {
|
||||||
|
|
||||||
|
val descriptor: MetaDescriptor? by lazy { properties.descriptor?.get(nodeName) }
|
||||||
|
|
||||||
|
|
||||||
|
override val items: Map<NameToken, MutableMeta>
|
||||||
|
get() {
|
||||||
|
val metaKeys = properties.own?.getMeta(nodeName)?.items?.keys ?: emptySet()
|
||||||
|
val descriptorKeys = descriptor?.children?.map { NameToken(it.key) } ?: emptySet()
|
||||||
|
val defaultKeys = default?.get(nodeName)?.items?.keys ?: emptySet()
|
||||||
|
val inheritFlag = descriptor?.inherited ?: inherit
|
||||||
|
val stylesFlag = descriptor?.usesStyles ?: useStyles
|
||||||
|
return (metaKeys + descriptorKeys + defaultKeys).associateWith {
|
||||||
|
VisionPropertiesItem(
|
||||||
|
properties,
|
||||||
|
nodeName + it,
|
||||||
|
inheritFlag,
|
||||||
|
stylesFlag,
|
||||||
|
default
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override var value: Value?
|
||||||
|
get() {
|
||||||
|
val inheritFlag = descriptor?.inherited ?: inherit ?: false
|
||||||
|
val stylesFlag = descriptor?.usesStyles ?: useStyles ?: true
|
||||||
|
return properties.getValue(nodeName, inheritFlag, stylesFlag) ?: default?.getValue(nodeName)
|
||||||
|
}
|
||||||
|
set(value) {
|
||||||
|
properties.setValue(nodeName, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getOrCreate(name: Name): MutableMeta = VisionPropertiesItem(
|
||||||
|
properties,
|
||||||
|
nodeName + name,
|
||||||
|
inherit,
|
||||||
|
useStyles,
|
||||||
|
default
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun setMeta(name: Name, node: Meta?) {
|
||||||
|
properties.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)
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class AbstractVisionProperties(
|
||||||
|
private val vision: Vision,
|
||||||
|
) : MutableVisionProperties {
|
||||||
|
override val descriptor: MetaDescriptor? get() = vision.descriptor
|
||||||
|
|
||||||
|
protected abstract var properties: MutableMeta?
|
||||||
|
|
||||||
|
override val own: Meta? get() = properties
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
protected fun getOrCreateProperties(): MutableMeta {
|
||||||
|
if (properties == null) {
|
||||||
|
//TODO check performance issues
|
||||||
|
val newProperties = MutableMeta()
|
||||||
|
properties = newProperties
|
||||||
|
}
|
||||||
|
return properties!!
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getValue(
|
||||||
|
name: Name,
|
||||||
|
inherit: Boolean?,
|
||||||
|
includeStyles: Boolean?,
|
||||||
|
): Value? {
|
||||||
|
own?.get(name)?.value?.let { return it }
|
||||||
|
|
||||||
|
val descriptor = descriptor?.get(name)
|
||||||
|
val stylesFlag = includeStyles ?: descriptor?.usesStyles ?: true
|
||||||
|
|
||||||
|
if (stylesFlag) {
|
||||||
|
vision.getStyleProperty(name)?.value?.let { return it }
|
||||||
|
}
|
||||||
|
|
||||||
|
val inheritFlag = inherit ?: descriptor?.inherited ?: false
|
||||||
|
if (inheritFlag) {
|
||||||
|
vision.parent?.properties?.getValue(name, inheritFlag, stylesFlag)?.let { return it }
|
||||||
|
}
|
||||||
|
return descriptor?.defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setProperty(name: Name, node: Meta?, notify: Boolean) {
|
||||||
|
//TODO check old value?
|
||||||
|
if (name.isEmpty()) {
|
||||||
|
properties = node?.asMutableMeta()
|
||||||
|
} else if (node == null) {
|
||||||
|
properties?.setMeta(name, node)
|
||||||
|
} else {
|
||||||
|
getOrCreateProperties().setMeta(name, node)
|
||||||
|
}
|
||||||
|
if (notify) {
|
||||||
|
invalidate(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setValue(name: Name, value: Value?, notify: Boolean) {
|
||||||
|
//TODO check old value?
|
||||||
|
if (value == null) {
|
||||||
|
properties?.getMeta(name)?.value = null
|
||||||
|
} else {
|
||||||
|
getOrCreateProperties().setValue(name, value)
|
||||||
|
}
|
||||||
|
if (notify) {
|
||||||
|
invalidate(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transient
|
||||||
|
protected val changesInternal: MutableSharedFlow<Name> = MutableSharedFlow<Name>()
|
||||||
|
override val changes: SharedFlow<Name> get() = changesInternal
|
||||||
|
|
||||||
|
override fun invalidate(propertyName: Name) {
|
||||||
|
//send update signal
|
||||||
|
@OptIn(DelicateCoroutinesApi::class)
|
||||||
|
(vision.manager?.context ?: GlobalScope).launch {
|
||||||
|
changesInternal.emit(propertyName)
|
||||||
|
}
|
||||||
|
|
||||||
|
//notify children if there are any
|
||||||
|
if (vision is VisionGroup) {
|
||||||
|
vision.children.values.forEach {
|
||||||
|
it.properties.invalidate(propertyName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// update styles
|
||||||
|
if (propertyName == Vision.STYLE_KEY) {
|
||||||
|
vision.styles.asSequence()
|
||||||
|
.mapNotNull { vision.getStyle(it) }
|
||||||
|
.flatMap { it.items.asSequence() }
|
||||||
|
.distinctBy { it.key }
|
||||||
|
.forEach {
|
||||||
|
invalidate(it.key.asName())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun VisionProperties.getValue(
|
||||||
|
name: String,
|
||||||
|
inherit: Boolean? = null,
|
||||||
|
includeStyles: Boolean? = null,
|
||||||
|
): Value? = getValue(name.parseAsName(), inherit, includeStyles)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get [Vision] property using key as a String
|
||||||
|
*/
|
||||||
|
public fun VisionProperties.getProperty(
|
||||||
|
name: String,
|
||||||
|
inherit: Boolean? = null,
|
||||||
|
includeStyles: Boolean? = null,
|
||||||
|
): Meta = getProperty(name.parseAsName(), inherit, includeStyles)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The root property node with given inheritance and style flags
|
||||||
|
* @param inherit - inherit properties from the [Vision] parent. If null, infer from descriptor
|
||||||
|
* @param includeStyles - include style information. If null, infer from descriptor
|
||||||
|
*/
|
||||||
|
public fun MutableVisionProperties.root(
|
||||||
|
inherit: Boolean? = null,
|
||||||
|
includeStyles: Boolean? = null,
|
||||||
|
): MutableMeta = getProperty(Name.EMPTY, inherit, includeStyles)
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get [Vision] property using key as a String
|
||||||
|
*/
|
||||||
|
public fun MutableVisionProperties.getProperty(
|
||||||
|
name: String,
|
||||||
|
inherit: Boolean? = null,
|
||||||
|
includeStyles: Boolean? = null,
|
||||||
|
): MutableMeta = getProperty(name.parseAsName(), inherit, includeStyles)
|
||||||
|
|
||||||
|
|
||||||
|
public operator fun MutableVisionProperties.set(name: Name, value: Number): Unit =
|
||||||
|
setValue(name, value.asValue())
|
||||||
|
|
||||||
|
public operator fun MutableVisionProperties.set(name: String, value: Number): Unit =
|
||||||
|
set(name.parseAsName(), value)
|
||||||
|
|
||||||
|
public operator fun MutableVisionProperties.set(name: Name, value: Boolean): Unit =
|
||||||
|
setValue(name, value.asValue())
|
||||||
|
|
||||||
|
public operator fun MutableVisionProperties.set(name: String, value: Boolean): Unit =
|
||||||
|
set(name.parseAsName(), value)
|
||||||
|
|
||||||
|
public operator fun MutableVisionProperties.set(name: Name, value: String): Unit =
|
||||||
|
setValue(name, value.asValue())
|
||||||
|
|
||||||
|
public operator fun MutableVisionProperties.set(name: String, value: String): Unit =
|
||||||
|
set(name.parseAsName(), value)
|
||||||
|
|
@ -1,34 +0,0 @@
|
|||||||
package space.kscience.visionforge
|
|
||||||
|
|
||||||
import space.kscience.dataforge.meta.Configurable
|
|
||||||
import space.kscience.dataforge.meta.MutableMeta
|
|
||||||
import space.kscience.dataforge.meta.ObservableMutableMeta
|
|
||||||
import space.kscience.dataforge.meta.get
|
|
||||||
import space.kscience.dataforge.names.Name
|
|
||||||
import space.kscience.dataforge.values.Value
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Property containers are used to create a symmetric behaviors for vision properties and style builders
|
|
||||||
*/
|
|
||||||
public interface VisionPropertyContainer<out V : Vision> {
|
|
||||||
|
|
||||||
public val meta: MutableMeta
|
|
||||||
|
|
||||||
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,
|
|
||||||
inherit: Boolean,
|
|
||||||
includeStyles: Boolean,
|
|
||||||
includeDefaults: Boolean
|
|
||||||
): Value? = meta[name]?.value
|
|
||||||
}
|
|
@ -0,0 +1,55 @@
|
|||||||
|
package space.kscience.visionforge
|
||||||
|
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.flow
|
||||||
|
import space.kscience.dataforge.meta.Meta
|
||||||
|
import space.kscience.dataforge.meta.Value
|
||||||
|
import space.kscience.dataforge.names.Name
|
||||||
|
import space.kscience.dataforge.names.parseAsName
|
||||||
|
import space.kscience.dataforge.names.startsWith
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a flow of a specific property
|
||||||
|
*/
|
||||||
|
public fun Vision.flowProperty(
|
||||||
|
propertyName: Name,
|
||||||
|
inherit: Boolean? = null,
|
||||||
|
includeStyles: Boolean? = null,
|
||||||
|
): Flow<Meta> = flow {
|
||||||
|
//Pass initial value.
|
||||||
|
emit(properties.getProperty(propertyName, inherit, includeStyles))
|
||||||
|
properties.changes.collect { name ->
|
||||||
|
if (name.startsWith(propertyName)) {
|
||||||
|
emit(properties.getProperty(propertyName, inherit, includeStyles))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun Vision.flowProperty(
|
||||||
|
propertyName: String,
|
||||||
|
inherit: Boolean? = null,
|
||||||
|
includeStyles: Boolean? = null,
|
||||||
|
): Flow<Meta> = flowProperty(propertyName.parseAsName(), inherit, includeStyles)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flow the value of specific property
|
||||||
|
*/
|
||||||
|
public fun Vision.flowPropertyValue(
|
||||||
|
propertyName: Name,
|
||||||
|
inherit: Boolean? = null,
|
||||||
|
includeStyles: Boolean? = null,
|
||||||
|
): Flow<Value?> = flow {
|
||||||
|
//Pass initial value.
|
||||||
|
emit(properties.getValue(propertyName, inherit, includeStyles))
|
||||||
|
properties.changes.collect { name ->
|
||||||
|
if (name.startsWith(propertyName)) {
|
||||||
|
emit(properties.getValue(propertyName, inherit, includeStyles))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun Vision.flowPropertyValue(
|
||||||
|
propertyName: String,
|
||||||
|
inherit: Boolean? = null,
|
||||||
|
includeStyles: Boolean? = null,
|
||||||
|
): Flow<Value?> = flowPropertyValue(propertyName.parseAsName(), inherit, includeStyles)
|
@ -22,7 +22,7 @@ internal const val RENDER_FUNCTION_NAME = "renderAllVisionsById"
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Render a fragment in the given consumer and return a map of extracted visions
|
* Render a fragment in the given consumer and return a map of extracted visions
|
||||||
* @param manager a VisionManager used for serialization
|
* @param context a context used to create a vision fragment
|
||||||
* @param embedData embed Vision initial state in the HTML
|
* @param embedData embed Vision initial state in the HTML
|
||||||
* @param fetchDataUrl fetch data after first render from given url
|
* @param fetchDataUrl fetch data after first render from given url
|
||||||
* @param fetchUpdatesUrl receive push updates from the server at given url
|
* @param fetchUpdatesUrl receive push updates from the server at given url
|
||||||
|
@ -15,7 +15,7 @@ import space.kscience.dataforge.meta.node
|
|||||||
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 mutableProperties.node()
|
||||||
}
|
}
|
||||||
|
|
||||||
public class HtmlFormFragment internal constructor(
|
public class HtmlFormFragment internal constructor(
|
||||||
|
@ -5,11 +5,15 @@ 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.dataforge.names.Name
|
||||||
|
import space.kscience.visionforge.*
|
||||||
|
|
||||||
|
//TODO replace by something
|
||||||
|
internal val Vision.mutableProperties get() = properties.getProperty(Name.EMPTY, false, false)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
public abstract class VisionOfHtmlInput : VisionBase() {
|
public abstract class VisionOfHtmlInput : AbstractVision() {
|
||||||
public var disabled: Boolean by meta.boolean(false)
|
public var disabled: Boolean by mutableProperties.boolean { false }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@ -18,7 +22,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 mutableProperties.string()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@ -27,7 +31,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 mutableProperties.boolean()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@ -36,7 +40,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 mutableProperties.number()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@ -48,6 +52,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 mutableProperties.number()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +86,9 @@ public abstract class VisionTagConsumer<R>(
|
|||||||
): T = div {
|
): T = div {
|
||||||
id = resolveId(name)
|
id = resolveId(name)
|
||||||
classes = setOf(OUTPUT_CLASS)
|
classes = setOf(OUTPUT_CLASS)
|
||||||
vision.setAsRoot(manager)
|
if (vision.parent == null) {
|
||||||
|
vision.setAsRoot(manager)
|
||||||
|
}
|
||||||
attributes[OUTPUT_NAME_ATTRIBUTE] = name.toString()
|
attributes[OUTPUT_NAME_ATTRIBUTE] = name.toString()
|
||||||
if (!outputMeta.isEmpty()) {
|
if (!outputMeta.isEmpty()) {
|
||||||
//Hard-code output configuration
|
//Hard-code output configuration
|
||||||
|
@ -0,0 +1,54 @@
|
|||||||
|
package space.kscience.visionforge
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import space.kscience.dataforge.meta.Meta
|
||||||
|
import space.kscience.dataforge.names.Name
|
||||||
|
import space.kscience.dataforge.names.asName
|
||||||
|
import space.kscience.dataforge.names.parseAsName
|
||||||
|
import space.kscience.dataforge.names.startsWith
|
||||||
|
import kotlin.reflect.KProperty1
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call [callBack] on initial value of the property and then on all subsequent values after change
|
||||||
|
*/
|
||||||
|
public fun Vision.useProperty(
|
||||||
|
propertyName: Name,
|
||||||
|
inherit: Boolean? = null,
|
||||||
|
includeStyles: Boolean? = null,
|
||||||
|
scope: CoroutineScope? = manager?.context,
|
||||||
|
callBack: (Meta) -> Unit,
|
||||||
|
): Job {
|
||||||
|
//Pass initial value.
|
||||||
|
callBack(properties.getProperty(propertyName, inherit, includeStyles))
|
||||||
|
return properties.changes.onEach { name ->
|
||||||
|
if (name.startsWith(propertyName)) {
|
||||||
|
callBack(properties.getProperty(propertyName, inherit, includeStyles))
|
||||||
|
}
|
||||||
|
}.launchIn(scope ?: error("Orphan Vision can't observe properties"))
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun Vision.useProperty(
|
||||||
|
propertyName: String,
|
||||||
|
inherit: Boolean? = null,
|
||||||
|
includeStyles: Boolean? = null,
|
||||||
|
scope: CoroutineScope? = manager?.context,
|
||||||
|
callBack: (Meta) -> Unit,
|
||||||
|
): Job = useProperty(propertyName.parseAsName(), inherit, includeStyles, scope, callBack)
|
||||||
|
|
||||||
|
public fun <V : Vision, T> V.useProperty(
|
||||||
|
property: KProperty1<V, T>,
|
||||||
|
scope: CoroutineScope? = manager?.context,
|
||||||
|
callBack: V.(T) -> Unit,
|
||||||
|
): Job {
|
||||||
|
//Pass initial value.
|
||||||
|
callBack(property.get(this))
|
||||||
|
return properties.changes.onEach { name ->
|
||||||
|
if (name.startsWith(property.name.asName())) {
|
||||||
|
callBack(property.get(this@useProperty))
|
||||||
|
}
|
||||||
|
}.launchIn(scope ?: error("Orphan Vision can't observe properties"))
|
||||||
|
}
|
@ -1,91 +0,0 @@
|
|||||||
package space.kscience.visionforge
|
|
||||||
|
|
||||||
import space.kscience.dataforge.names.Name
|
|
||||||
import space.kscience.dataforge.values.Value
|
|
||||||
import space.kscience.dataforge.values.number
|
|
||||||
import kotlin.properties.ReadWriteProperty
|
|
||||||
import kotlin.reflect.KProperty
|
|
||||||
|
|
||||||
//public fun Vision.propertyNode(
|
|
||||||
// name: Name? = null,
|
|
||||||
// inherit: Boolean = false,
|
|
||||||
// includeStyles: Boolean = true,
|
|
||||||
// includeDefaults: Boolean = true,
|
|
||||||
//): ReadWriteProperty<Any?, Meta?> = object : ReadWriteProperty<Any?, Meta?> {
|
|
||||||
// override fun getValue(thisRef: Any?, property: KProperty<*>): Meta? =
|
|
||||||
// getProperty(name ?: Name.parse(property.name), inherit, includeStyles, includeDefaults)
|
|
||||||
//
|
|
||||||
// override fun setValue(thisRef: Any?, property: KProperty<*>, value: Meta?) {
|
|
||||||
// meta.setMeta(name ?: Name.parse(property.name), value)
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//public fun <T> Vision.propertyNode(
|
|
||||||
// converter: MetaConverter<T>,
|
|
||||||
// name: Name? = null,
|
|
||||||
// inherit: Boolean = false,
|
|
||||||
// includeStyles: Boolean = true,
|
|
||||||
// includeDefaults: Boolean = true,
|
|
||||||
//): ReadWriteProperty<Any?, T?> = object : ReadWriteProperty<Any?, T?> {
|
|
||||||
// override fun getValue(thisRef: Any?, property: KProperty<*>): T? = getProperty(
|
|
||||||
// name ?: Name.parse(property.name),
|
|
||||||
// inherit,
|
|
||||||
// includeStyles,
|
|
||||||
// includeDefaults
|
|
||||||
// )?.let(converter::metaToObject)
|
|
||||||
//
|
|
||||||
// override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
|
|
||||||
// meta.setMeta(name ?: Name.parse(property.name), value?.let(converter::objectToMeta))
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
public fun Vision.propertyValue(
|
|
||||||
name: Name? = null,
|
|
||||||
inherit: Boolean = false,
|
|
||||||
includeStyles: Boolean = true,
|
|
||||||
includeDefaults: Boolean = true,
|
|
||||||
): ReadWriteProperty<Any?, Value?> = object : ReadWriteProperty<Any?, Value?> {
|
|
||||||
override fun getValue(thisRef: Any?, property: KProperty<*>): Value? =
|
|
||||||
getPropertyValue(name ?: Name.parse(property.name), inherit, includeStyles, includeDefaults)
|
|
||||||
|
|
||||||
override fun setValue(thisRef: Any?, property: KProperty<*>, value: Value?) {
|
|
||||||
meta.setValue(name ?: Name.parse(property.name), value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun <T> Vision.propertyValue(
|
|
||||||
name: Name? = null,
|
|
||||||
inherit: Boolean = false,
|
|
||||||
includeStyles: Boolean = true,
|
|
||||||
includeDefaults: Boolean = true,
|
|
||||||
setter: (T) -> Value? = { it?.let(Value::of) },
|
|
||||||
getter: (Value?) -> T,
|
|
||||||
): ReadWriteProperty<Any?, T> = object : ReadWriteProperty<Any?, T> {
|
|
||||||
override fun getValue(thisRef: Any?, property: KProperty<*>): T = getPropertyValue(
|
|
||||||
name ?: Name.parse(property.name),
|
|
||||||
inherit,
|
|
||||||
includeStyles,
|
|
||||||
includeDefaults
|
|
||||||
).let(getter)
|
|
||||||
|
|
||||||
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
|
|
||||||
meta.setValue(name ?: Name.parse(property.name), value?.let(setter))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun Vision.numberProperty(
|
|
||||||
name: Name? = null,
|
|
||||||
inherit: Boolean = false,
|
|
||||||
includeStyles: Boolean = true,
|
|
||||||
includeDefaults: Boolean = true
|
|
||||||
): ReadWriteProperty<Any?, Number?> = propertyValue(name, inherit, includeStyles, includeDefaults) { it?.number }
|
|
||||||
|
|
||||||
public fun Vision.numberProperty(
|
|
||||||
name: Name? = null,
|
|
||||||
inherit: Boolean = false,
|
|
||||||
includeStyles: Boolean = true,
|
|
||||||
includeDefaults: Boolean = true,
|
|
||||||
default: () -> Number
|
|
||||||
): ReadWriteProperty<Any?, Number> = propertyValue(name, inherit, includeStyles, includeDefaults) {
|
|
||||||
it?.number ?: default()
|
|
||||||
}
|
|
@ -2,7 +2,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.meta.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"
|
||||||
|
@ -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){
|
||||||
|
@ -6,7 +6,8 @@ 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.children
|
||||||
|
import space.kscience.visionforge.forEach
|
||||||
|
|
||||||
public interface VisionVisitor {
|
public interface VisionVisitor {
|
||||||
/**
|
/**
|
||||||
@ -19,30 +20,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)
|
|
||||||
}
|
vision.children?.forEach { token, child ->
|
||||||
|
visitTreeAsync(visionVisitor, name + token, child)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recursively visit this [Vision] and all children
|
* Recursively visit this [Vision] and all children
|
||||||
*/
|
*/
|
||||||
|
@ -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
|
||||||
|
|
||||||
@ -34,9 +30,10 @@ fun FlowContent.renderVisionFragment(
|
|||||||
|
|
||||||
|
|
||||||
@DFExperimental
|
@DFExperimental
|
||||||
class HtmlTagTest {
|
private fun VisionOutput.base(block: VisionGroup.() -> Unit) = context.visionManager.group().apply(block)
|
||||||
|
|
||||||
fun VisionOutput.base(block: VisionBase.() -> Unit) = VisionBase().apply(block)
|
@DFExperimental
|
||||||
|
class HtmlTagTest {
|
||||||
|
|
||||||
val fragment: HtmlVisionFragment = {
|
val fragment: HtmlVisionFragment = {
|
||||||
div {
|
div {
|
||||||
@ -46,10 +43,8 @@ class HtmlTagTest {
|
|||||||
"metaProperty" put 87
|
"metaProperty" put 87
|
||||||
}
|
}
|
||||||
base {
|
base {
|
||||||
configure {
|
properties["myProp"] = 82
|
||||||
set("myProp", 82)
|
properties["otherProp"] = false
|
||||||
set("otherProp", false)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -59,7 +54,7 @@ class HtmlTagTest {
|
|||||||
div {
|
div {
|
||||||
h2 { +"Properties" }
|
h2 { +"Properties" }
|
||||||
ul {
|
ul {
|
||||||
(vision as? VisionBase)?.meta?.items?.forEach {
|
vision.properties.own?.items?.forEach {
|
||||||
li {
|
li {
|
||||||
a { +it.key.toString() }
|
a { +it.key.toString() }
|
||||||
p { +it.value.toString() }
|
p { +it.value.toString() }
|
||||||
|
@ -1,44 +1,123 @@
|
|||||||
package space.kscience.visionforge.meta
|
package space.kscience.visionforge.meta
|
||||||
|
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.take
|
||||||
|
import kotlinx.coroutines.flow.toList
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import space.kscience.dataforge.context.Global
|
||||||
|
import space.kscience.dataforge.context.fetch
|
||||||
import space.kscience.dataforge.meta.*
|
import space.kscience.dataforge.meta.*
|
||||||
import space.kscience.dataforge.values.asValue
|
import space.kscience.visionforge.*
|
||||||
import space.kscience.visionforge.VisionBase
|
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertNotEquals
|
import kotlin.test.assertNotEquals
|
||||||
|
|
||||||
class VisionPropertyTest {
|
|
||||||
@Test
|
|
||||||
fun testPropertyWrite(){
|
|
||||||
val vision = VisionBase()
|
|
||||||
vision.meta["fff"] = 2
|
|
||||||
vision.meta["fff.ddd"] = false
|
|
||||||
|
|
||||||
assertEquals(2, vision.meta["fff"]?.int)
|
private class TestScheme : Scheme() {
|
||||||
assertEquals(false, vision.meta["fff.ddd"]?.boolean)
|
var ddd by int()
|
||||||
|
|
||||||
|
companion object : SchemeSpec<TestScheme>(::TestScheme)
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
internal class VisionPropertyTest {
|
||||||
|
|
||||||
|
private val manager = Global.fetch(VisionManager)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testPropertyWrite() {
|
||||||
|
val vision = manager.group()
|
||||||
|
vision.properties["fff"] = 2
|
||||||
|
vision.properties["fff.ddd"] = false
|
||||||
|
|
||||||
|
assertEquals(2, vision.properties.getValue("fff")?.int)
|
||||||
|
assertEquals(false, vision.properties.getValue("fff.ddd")?.boolean)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testPropertyEdit(){
|
fun testPropertyEdit() {
|
||||||
val vision = VisionBase()
|
val vision = manager.group()
|
||||||
vision.meta.getOrCreate("fff.ddd").apply {
|
vision.properties.getProperty("fff.ddd").apply {
|
||||||
value = 2.asValue()
|
value = 2.asValue()
|
||||||
}
|
}
|
||||||
assertEquals(2, vision.meta["fff.ddd"]?.int)
|
assertEquals(2, vision.properties.getValue("fff.ddd")?.int)
|
||||||
assertNotEquals(true, vision.meta["fff.ddd"]?.boolean)
|
assertNotEquals(true, vision.properties.getValue("fff.ddd")?.boolean)
|
||||||
}
|
|
||||||
|
|
||||||
internal class TestScheme: Scheme(){
|
|
||||||
var ddd by int()
|
|
||||||
companion object: SchemeSpec<TestScheme>(::TestScheme)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testPropertyUpdate(){
|
fun testPropertyUpdate() {
|
||||||
val vision = VisionBase()
|
val vision = manager.group()
|
||||||
vision.meta.getOrCreate("fff").updateWith(TestScheme){
|
vision.properties.getProperty("fff").updateWith(TestScheme) {
|
||||||
ddd = 2
|
ddd = 2
|
||||||
}
|
}
|
||||||
assertEquals(2, vision.meta["fff.ddd"]?.int)
|
assertEquals(2, vision.properties.getValue("fff.ddd")?.int)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testChildrenPropertyPropagation() = runTest(dispatchTimeoutMs = 200) {
|
||||||
|
val group = Global.fetch(VisionManager).group {
|
||||||
|
properties {
|
||||||
|
"test" put 11
|
||||||
|
}
|
||||||
|
group("child") {
|
||||||
|
properties {
|
||||||
|
"test" put 22
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val child = group.children["child"]!!
|
||||||
|
|
||||||
|
val deferred: CompletableDeferred<Value?> = CompletableDeferred()
|
||||||
|
|
||||||
|
var callCounter = 0
|
||||||
|
|
||||||
|
val subscription = child.useProperty("test", inherit = true) {
|
||||||
|
deferred.complete(it.value)
|
||||||
|
callCounter++
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(22, deferred.await()?.int)
|
||||||
|
assertEquals(1, callCounter)
|
||||||
|
|
||||||
|
child.properties.remove("test")
|
||||||
|
|
||||||
|
assertEquals(11, child.properties.getProperty("test", inherit = true).int)
|
||||||
|
// assertEquals(11, deferred.await()?.int)
|
||||||
|
// assertEquals(2, callCounter)
|
||||||
|
subscription.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testChildrenPropertyFlow() = runTest(dispatchTimeoutMs = 200) {
|
||||||
|
val group = Global.fetch(VisionManager).group {
|
||||||
|
|
||||||
|
properties {
|
||||||
|
"test" put 11
|
||||||
|
}
|
||||||
|
|
||||||
|
group("child") {
|
||||||
|
properties {
|
||||||
|
"test" put 22
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
val child = group.children["child"]!!
|
||||||
|
|
||||||
|
launch {
|
||||||
|
val list = child.flowPropertyValue("test", inherit = true).take(3).map { it?.int }.toList()
|
||||||
|
assertEquals(22, list.first())
|
||||||
|
//assertEquals(11, list[1]) //a race
|
||||||
|
assertEquals(33, list.last())
|
||||||
|
}
|
||||||
|
|
||||||
|
//wait for subscription to be created
|
||||||
|
delay(5)
|
||||||
|
|
||||||
|
child.properties.remove("test")
|
||||||
|
group.properties["test"] = 33
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -65,6 +65,7 @@ public class VisionClient : AbstractPlugin() {
|
|||||||
|
|
||||||
private fun renderVision(name: String, element: Element, vision: Vision?, outputMeta: Meta) {
|
private fun renderVision(name: String, element: Element, vision: Vision?, outputMeta: Meta) {
|
||||||
if (vision != null) {
|
if (vision != null) {
|
||||||
|
vision.setAsRoot(visionManager)
|
||||||
val renderer = findRendererFor(vision)
|
val renderer = findRendererFor(vision)
|
||||||
?: error("Could not find renderer for ${visionManager.encodeToString(vision)}")
|
?: error("Could not find renderer for ${visionManager.encodeToString(vision)}")
|
||||||
renderer.render(element, vision, outputMeta)
|
renderer.render(element, vision, outputMeta)
|
||||||
@ -115,7 +116,7 @@ public class VisionClient : AbstractPlugin() {
|
|||||||
|
|
||||||
onopen = {
|
onopen = {
|
||||||
feedbackJob = vision.flowChanges(
|
feedbackJob = vision.flowChanges(
|
||||||
feedbackAggregationTime.milliseconds
|
feedbackAggregationTime.milliseconds,
|
||||||
).onEach { change ->
|
).onEach { change ->
|
||||||
send(visionManager.encodeToString(change))
|
send(visionManager.encodeToString(change))
|
||||||
}.launchIn(visionManager.context)
|
}.launchIn(visionManager.context)
|
||||||
@ -203,8 +204,7 @@ public class VisionClient : AbstractPlugin() {
|
|||||||
) else super.content(target)
|
) else super.content(target)
|
||||||
|
|
||||||
public companion object : PluginFactory<VisionClient> {
|
public companion object : PluginFactory<VisionClient> {
|
||||||
|
override fun build(context: Context, meta: Meta): VisionClient = VisionClient()
|
||||||
override fun invoke(meta: Meta, context: Context): VisionClient = VisionClient()
|
|
||||||
|
|
||||||
override val tag: PluginTag = PluginTag(name = "vision.client", group = PluginTag.DATAFORGE_GROUP)
|
override val tag: PluginTag = PluginTag(name = "vision.client", group = PluginTag.DATAFORGE_GROUP)
|
||||||
|
|
||||||
|
@ -0,0 +1,96 @@
|
|||||||
|
package space.kscience.visionforge.html
|
||||||
|
|
||||||
|
import kotlinx.html.*
|
||||||
|
import space.kscience.dataforge.context.ContextAware
|
||||||
|
import space.kscience.dataforge.meta.Meta
|
||||||
|
import space.kscience.dataforge.meta.MetaSerializer
|
||||||
|
import space.kscience.dataforge.meta.isEmpty
|
||||||
|
import space.kscience.dataforge.misc.DFExperimental
|
||||||
|
import space.kscience.dataforge.names.Name
|
||||||
|
import space.kscience.dataforge.names.NameToken
|
||||||
|
import space.kscience.dataforge.names.asName
|
||||||
|
import space.kscience.dataforge.names.parseAsName
|
||||||
|
import space.kscience.visionforge.Vision
|
||||||
|
import space.kscience.visionforge.VisionManager
|
||||||
|
import space.kscience.visionforge.setAsRoot
|
||||||
|
import space.kscience.visionforge.visionManager
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rendering context for visions in HTML
|
||||||
|
*/
|
||||||
|
public interface HtmlVisionContext : ContextAware {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate div id for vision div tag
|
||||||
|
*/
|
||||||
|
public fun generateId(name: Name): String = "vision[$name]"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render vision at given [DIV]
|
||||||
|
*/
|
||||||
|
public fun DIV.renderVision(name: Name, vision: Vision, outputMeta: Meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public typealias HtmlVisionContextFragment = context(HtmlVisionContext) TagConsumer<*>.() -> Unit
|
||||||
|
|
||||||
|
context(HtmlVisionContext)
|
||||||
|
public fun HtmlVisionContextFragment(content: TagConsumer<*>.() -> Unit): HtmlVisionFragment = content
|
||||||
|
|
||||||
|
context(HtmlVisionContext)
|
||||||
|
private fun <T> TagConsumer<T>.vision(
|
||||||
|
visionManager: VisionManager,
|
||||||
|
name: Name,
|
||||||
|
vision: Vision,
|
||||||
|
outputMeta: Meta = Meta.EMPTY,
|
||||||
|
): T = div {
|
||||||
|
id = generateId(name)
|
||||||
|
classes = setOf(VisionTagConsumer.OUTPUT_CLASS)
|
||||||
|
vision.setAsRoot(visionManager)
|
||||||
|
attributes[VisionTagConsumer.OUTPUT_NAME_ATTRIBUTE] = name.toString()
|
||||||
|
if (!outputMeta.isEmpty()) {
|
||||||
|
//Hard-code output configuration
|
||||||
|
script {
|
||||||
|
attributes["class"] = VisionTagConsumer.OUTPUT_META_CLASS
|
||||||
|
unsafe {
|
||||||
|
+visionManager.jsonFormat.encodeToString(MetaSerializer, outputMeta)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
renderVision(name, vision, outputMeta)
|
||||||
|
}
|
||||||
|
|
||||||
|
context(HtmlVisionContext)
|
||||||
|
private fun <T> TagConsumer<T>.vision(
|
||||||
|
name: Name,
|
||||||
|
vision: Vision,
|
||||||
|
outputMeta: Meta = Meta.EMPTY,
|
||||||
|
): T = vision(context.visionManager, name, vision, outputMeta)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert a vision in this HTML.
|
||||||
|
*/
|
||||||
|
context(HtmlVisionContext)
|
||||||
|
@DFExperimental
|
||||||
|
@VisionDSL
|
||||||
|
public fun <T> TagConsumer<T>.vision(
|
||||||
|
name: Name? = null,
|
||||||
|
visionProvider: VisionOutput.() -> Vision,
|
||||||
|
): T {
|
||||||
|
val output = VisionOutput(context, name)
|
||||||
|
val vision = output.visionProvider()
|
||||||
|
val actualName =
|
||||||
|
name ?: NameToken(VisionTagConsumer.DEFAULT_VISION_NAME, vision.hashCode().toUInt().toString()).asName()
|
||||||
|
return vision(output.buildVisionManager(), actualName, vision, output.meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert a vision in this HTML.
|
||||||
|
*/
|
||||||
|
context(HtmlVisionContext)
|
||||||
|
@DFExperimental
|
||||||
|
@VisionDSL
|
||||||
|
public fun <T> TagConsumer<T>.vision(
|
||||||
|
name: String?,
|
||||||
|
visionProvider: VisionOutput.() -> Vision,
|
||||||
|
): T = vision(name?.parseAsName(), visionProvider)
|
@ -1,12 +1,12 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id("ru.mipt.npm.gradle.jvm")
|
id("space.kscience.gradle.jvm")
|
||||||
}
|
}
|
||||||
|
|
||||||
val dataforgeVersion: String by rootProject.extra
|
val dataforgeVersion: String by rootProject.extra
|
||||||
val fxVersion: String by rootProject.extra
|
val fxVersion: String by rootProject.extra
|
||||||
|
|
||||||
kscience{
|
kscience{
|
||||||
useFx(ru.mipt.npm.gradle.FXModule.CONTROLS, version = fxVersion)
|
useFx(space.kscience.gradle.FXModule.CONTROLS, version = fxVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
@ -15,12 +15,12 @@ dependencies {
|
|||||||
api("org.fxyz3d:fxyz3d:0.5.4") {
|
api("org.fxyz3d:fxyz3d:0.5.4") {
|
||||||
exclude(module = "slf4j-simple")
|
exclude(module = "slf4j-simple")
|
||||||
}
|
}
|
||||||
api("org.jetbrains.kotlinx:kotlinx-coroutines-javafx:${ru.mipt.npm.gradle.KScienceVersions.coroutinesVersion}")
|
api("org.jetbrains.kotlinx:kotlinx-coroutines-javafx:${space.kscience.gradle.KScienceVersions.coroutinesVersion}")
|
||||||
implementation("eu.mihosoft.vrl.jcsg:jcsg:0.5.7") {
|
implementation("eu.mihosoft.vrl.jcsg:jcsg:0.5.7") {
|
||||||
exclude(module = "slf4j-simple")
|
exclude(module = "slf4j-simple")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
readme{
|
readme{
|
||||||
maturity = ru.mipt.npm.gradle.Maturity.PROTOTYPE
|
maturity = space.kscience.gradle.Maturity.PROTOTYPE
|
||||||
}
|
}
|
@ -95,8 +95,9 @@ public class FXPlugin(meta: Meta = Meta.EMPTY) : AbstractPlugin(meta) {
|
|||||||
public companion object : PluginFactory<FXPlugin> {
|
public companion object : PluginFactory<FXPlugin> {
|
||||||
override val type: KClass<out FXPlugin> = FXPlugin::class
|
override val type: KClass<out FXPlugin> = FXPlugin::class
|
||||||
override val tag: PluginTag = PluginTag("vis.fx", group = PluginTag.DATAFORGE_GROUP)
|
override val tag: PluginTag = PluginTag("vis.fx", group = PluginTag.DATAFORGE_GROUP)
|
||||||
override fun invoke(meta: Meta, context: Context): FXPlugin =
|
|
||||||
FXPlugin(meta)
|
override fun build(context: Context, meta: Meta): FXPlugin = FXPlugin(meta)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -3,13 +3,9 @@ package space.kscience.visionforge.editor
|
|||||||
import javafx.scene.control.ColorPicker
|
import javafx.scene.control.ColorPicker
|
||||||
import javafx.scene.paint.Color
|
import javafx.scene.paint.Color
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.*
|
||||||
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.values.Null
|
|
||||||
import space.kscience.dataforge.values.Value
|
|
||||||
import space.kscience.dataforge.values.asValue
|
|
||||||
import space.kscience.dataforge.values.string
|
|
||||||
import tornadofx.*
|
import tornadofx.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user