From 787c841a51b6a1779507dc4021ce97510c4bd653 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Wed, 21 Jul 2021 16:31:17 +0300 Subject: [PATCH 01/15] [WIP] new DF experiment --- build.gradle.kts | 4 +- settings.gradle.kts | 1 + .../space/kscience/visionforge/VisionBase.kt | 8 ++-- .../kscience/visionforge/VisionChange.kt | 2 +- .../visionforge/VisionPropertyContainer.kt | 7 +-- .../kscience/visionforge/schemeDesctiptors.kt | 5 ++- visionforge-markdown/build.gradle.kts | 44 +++++++++++++++++++ .../visionforge/markup/VisionOfMarkup.kt | 40 +++++++++++++++++ .../kscience/visionforge/markup/markdown.kt | 25 +++++++++++ .../visionforge/markup/MarkupPlugin.kt | 38 ++++++++++++++++ .../visionforge/plotly/VisionOfPlotly.kt | 1 - .../visionforge/three/server/VisionServer.kt | 2 +- .../kscience/visionforge/solid/Extruded.kt | 4 +- 13 files changed, 163 insertions(+), 18 deletions(-) create mode 100644 visionforge-markdown/build.gradle.kts create mode 100644 visionforge-markdown/src/commonMain/kotlin/space/kscience/visionforge/markup/VisionOfMarkup.kt create mode 100644 visionforge-markdown/src/commonMain/kotlin/space/kscience/visionforge/markup/markdown.kt create mode 100644 visionforge-markdown/src/jsMain/kotlin/space/kscience/visionforge/markup/MarkupPlugin.kt diff --git a/build.gradle.kts b/build.gradle.kts index 4ab2a7d5..c7fbc2d8 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { // kotlin("js") version(kotlinVersion) apply false } -val dataforgeVersion by extra("0.4.3") +val dataforgeVersion by extra("0.5.0-dev-2") val fxVersion by extra("11") allprojects { @@ -20,7 +20,7 @@ allprojects { } group = "space.kscience" - version = "0.2.0-dev-22" + version = "0.2.0-dev-23" } subprojects { diff --git a/settings.gradle.kts b/settings.gradle.kts index 6311841e..70f3606a 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -33,6 +33,7 @@ include( ":visionforge-gdml", ":visionforge-server", ":visionforge-plotly", + ":visionforge-markdown", ":demo:solid-showcase", ":demo:gdml", ":demo:muon-monitor", diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionBase.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionBase.kt index 78b75124..4dcffbca 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionBase.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionBase.kt @@ -30,13 +30,13 @@ internal data class PropertyListener( @SerialName("vision") public open class VisionBase( override @Transient var parent: VisionGroup? = null, - protected var properties: Config? = null + protected var properties: MutableItemProvider? = null ) : Vision { @Synchronized - protected fun getOrCreateProperties(): Config { + protected fun getOrCreateProperties(): MutableItemProvider { if (properties == null) { - val newProperties = Config() + val newProperties = MetaBuilder() properties = newProperties } return properties!! @@ -46,7 +46,7 @@ public open class VisionBase( * A fast accessor method to get own property (no inheritance or styles */ override fun getOwnProperty(name: Name): MetaItem? = if (name == Name.EMPTY) { - properties?.asMetaItem() + properties?.rootItem } else { properties?.getItem(name) } diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionChange.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionChange.kt index 06ccb7bc..aa50001a 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionChange.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionChange.kt @@ -21,7 +21,7 @@ public class VisionChangeBuilder : VisionContainerBuilder { private var reset: Boolean = false private var vision: Vision? = null - private val propertyChange = Config() + private val propertyChange = MetaBuilder() private val children: HashMap = HashMap() public fun isEmpty(): Boolean = propertyChange.isEmpty() && propertyChange.isEmpty() && children.isEmpty() diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionPropertyContainer.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionPropertyContainer.kt index 112295c9..3710175c 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionPropertyContainer.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionPropertyContainer.kt @@ -1,9 +1,6 @@ package space.kscience.visionforge -import space.kscience.dataforge.meta.Config -import space.kscience.dataforge.meta.MetaItem -import space.kscience.dataforge.meta.get -import space.kscience.dataforge.meta.set +import space.kscience.dataforge.meta.* import space.kscience.dataforge.names.Name /** @@ -20,7 +17,7 @@ public interface VisionPropertyContainer { public fun setProperty(name: Name, item: MetaItem?, notify: Boolean = true) } -public open class SimpleVisionPropertyContainer(protected val config: Config): VisionPropertyContainer{ +public open class SimpleVisionPropertyContainer(protected val config: ObservableMeta): VisionPropertyContainer{ override fun getProperty( name: Name, inherit: Boolean, diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/schemeDesctiptors.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/schemeDesctiptors.kt index 8f342b52..4187843f 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/schemeDesctiptors.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/schemeDesctiptors.kt @@ -2,10 +2,11 @@ package space.kscience.visionforge import space.kscience.dataforge.meta.Scheme import space.kscience.dataforge.meta.SchemeSpec +import space.kscience.dataforge.meta.asObservable import space.kscience.dataforge.meta.descriptors.NodeDescriptor import space.kscience.dataforge.meta.descriptors.NodeDescriptorBuilder import space.kscience.dataforge.meta.descriptors.ValueDescriptorBuilder -import space.kscience.dataforge.meta.toConfig +import space.kscience.dataforge.meta.toMutableMeta import space.kscience.dataforge.values.ValueType import kotlin.reflect.KProperty1 import kotlin.reflect.typeOf @@ -53,7 +54,7 @@ public inline fun NodeDescriptorBuilder.value( } public fun NodeDescriptor.copy(block: NodeDescriptorBuilder.() -> Unit = {}): NodeDescriptor { - return NodeDescriptorBuilder(toMeta().toConfig()).apply(block) + return NodeDescriptorBuilder(toMeta().toMutableMeta().asObservable()).apply(block) } public inline fun NodeDescriptorBuilder.scheme( diff --git a/visionforge-markdown/build.gradle.kts b/visionforge-markdown/build.gradle.kts new file mode 100644 index 00000000..210e4ee8 --- /dev/null +++ b/visionforge-markdown/build.gradle.kts @@ -0,0 +1,44 @@ +plugins { + id("ru.mipt.npm.gradle.mpp") +} + +val markdownVersion = "0.2.4" + +kscience { + useSerialization() +} + +kotlin { + js { + //binaries.library() + binaries.executable() + browser { + webpackTask { + outputFileName = "js/visionforge-markdown.js" + } + } + } + + jvm { + val processResourcesTaskName = + compilations[org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.MAIN_COMPILATION_NAME] + .processResourcesTaskName + } + + + val jsBrowserDistribution by tasks.getting + + tasks.getByName("jvmProcessResources") { + dependsOn(jsBrowserDistribution) + from(jsBrowserDistribution) + } + + sourceSets { + commonMain { + dependencies { + api(project(":visionforge-core")) + api("org.jetbrains:markdown:$markdownVersion") + } + } + } +} \ No newline at end of file diff --git a/visionforge-markdown/src/commonMain/kotlin/space/kscience/visionforge/markup/VisionOfMarkup.kt b/visionforge-markdown/src/commonMain/kotlin/space/kscience/visionforge/markup/VisionOfMarkup.kt new file mode 100644 index 00000000..689ed221 --- /dev/null +++ b/visionforge-markdown/src/commonMain/kotlin/space/kscience/visionforge/markup/VisionOfMarkup.kt @@ -0,0 +1,40 @@ +package space.kscience.visionforge.markup + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlinx.serialization.modules.SerializersModule +import kotlinx.serialization.modules.polymorphic +import kotlinx.serialization.modules.subclass +import space.kscience.dataforge.meta.string +import space.kscience.dataforge.names.Name +import space.kscience.dataforge.names.toName +import space.kscience.visionforge.Vision +import space.kscience.visionforge.VisionBase +import space.kscience.visionforge.setProperty + +@Serializable +@SerialName("vision.markup") +public class VisionOfMarkup( + public val format: String = COMMONMARK_FORMAT +) : VisionBase() { + + //TODO add templates + + public var content: String? + get() = getOwnProperty(CONTENT_PROPERTY_KEY).string + set(value) { + setProperty(CONTENT_PROPERTY_KEY, value) + } + + public companion object { + public val CONTENT_PROPERTY_KEY: Name = "content".toName() + public const val COMMONMARK_FORMAT: String = "markdown.commonmark" + public const val GFM_FORMAT: String = "markdown.gfm" + } +} + +internal val markupSerializersModule = SerializersModule { + polymorphic(Vision::class) { + subclass(VisionOfMarkup.serializer()) + } +} \ No newline at end of file diff --git a/visionforge-markdown/src/commonMain/kotlin/space/kscience/visionforge/markup/markdown.kt b/visionforge-markdown/src/commonMain/kotlin/space/kscience/visionforge/markup/markdown.kt new file mode 100644 index 00000000..0ac353e4 --- /dev/null +++ b/visionforge-markdown/src/commonMain/kotlin/space/kscience/visionforge/markup/markdown.kt @@ -0,0 +1,25 @@ +package space.kscience.visionforge.markup + +import kotlinx.html.TagConsumer +import kotlinx.html.div +import kotlinx.html.unsafe +import org.intellij.markdown.flavours.MarkdownFlavourDescriptor +import org.intellij.markdown.flavours.commonmark.CommonMarkFlavourDescriptor +import org.intellij.markdown.html.HtmlGenerator +import org.intellij.markdown.parser.MarkdownParser + +/** + * Render markdown inside kotlinx-html tag + */ +public fun TagConsumer.markdown( + flavour: MarkdownFlavourDescriptor = CommonMarkFlavourDescriptor(), + block: () -> String +): T { + val src = block() + val parsedTree = MarkdownParser(flavour).buildMarkdownTreeFromString(src) + return div("visionforge-markdown") { + unsafe { + +HtmlGenerator(src, parsedTree, flavour).generateHtml() + } + } +} \ No newline at end of file diff --git a/visionforge-markdown/src/jsMain/kotlin/space/kscience/visionforge/markup/MarkupPlugin.kt b/visionforge-markdown/src/jsMain/kotlin/space/kscience/visionforge/markup/MarkupPlugin.kt new file mode 100644 index 00000000..30b8b848 --- /dev/null +++ b/visionforge-markdown/src/jsMain/kotlin/space/kscience/visionforge/markup/MarkupPlugin.kt @@ -0,0 +1,38 @@ +package space.kscience.visionforge.markup + +import kotlinx.browser.document +import kotlinx.serialization.modules.SerializersModule +import org.w3c.dom.Element +import space.kscience.dataforge.context.Context +import space.kscience.dataforge.context.PluginFactory +import space.kscience.dataforge.context.PluginTag +import space.kscience.dataforge.meta.Meta +import space.kscience.visionforge.ElementVisionRenderer +import space.kscience.visionforge.Vision +import space.kscience.visionforge.VisionClient +import space.kscience.visionforge.VisionPlugin +import kotlin.reflect.KClass + +public class MarkupPlugin : VisionPlugin(), ElementVisionRenderer { + public val visionClient: VisionClient by require(VisionClient) + override val tag: PluginTag get() = Companion.tag + override val visionSerializersModule: SerializersModule get() = markupSerializersModule + + override fun rateVision(vision: Vision): Int = when (vision) { + is VisionOfMarkup -> ElementVisionRenderer.DEFAULT_RATING + else -> ElementVisionRenderer.ZERO_RATING + } + + override fun render(element: Element, vision: Vision, meta: Meta) { + require(vision is VisionOfMarkup) { "The vision is not a markup vision" } + val div = document.createElement("div") + element.append(div) + TODO() + } + + public companion object : PluginFactory { + override val tag: PluginTag = PluginTag("vision.markup", PluginTag.DATAFORGE_GROUP) + override val type: KClass = MarkupPlugin::class + override fun invoke(meta: Meta, context: Context): MarkupPlugin = MarkupPlugin() + } +} \ No newline at end of file diff --git a/visionforge-plotly/src/commonMain/kotlin/space/kscience/visionforge/plotly/VisionOfPlotly.kt b/visionforge-plotly/src/commonMain/kotlin/space/kscience/visionforge/plotly/VisionOfPlotly.kt index 773ff4bf..8603971e 100644 --- a/visionforge-plotly/src/commonMain/kotlin/space/kscience/visionforge/plotly/VisionOfPlotly.kt +++ b/visionforge-plotly/src/commonMain/kotlin/space/kscience/visionforge/plotly/VisionOfPlotly.kt @@ -2,7 +2,6 @@ package space.kscience.visionforge.plotly import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import space.kscience.dataforge.meta.Config import space.kscience.dataforge.misc.DFExperimental import space.kscience.plotly.Plot import space.kscience.plotly.Plotly diff --git a/visionforge-server/src/main/kotlin/space/kscience/visionforge/three/server/VisionServer.kt b/visionforge-server/src/main/kotlin/space/kscience/visionforge/three/server/VisionServer.kt index 2c9ed631..fabb0675 100644 --- a/visionforge-server/src/main/kotlin/space/kscience/visionforge/three/server/VisionServer.kt +++ b/visionforge-server/src/main/kotlin/space/kscience/visionforge/three/server/VisionServer.kt @@ -54,7 +54,7 @@ public class VisionServer internal constructor( private val application: Application, private val rootRoute: String, ) : Configurable, CoroutineScope by application { - override val config: Config = Config() + override val config: ObservableMeta = ObservableMeta() public var updateInterval: Long by config.long(300, key = UPDATE_INTERVAL_KEY) public var cacheFragments: Boolean by config.boolean(true) public var dataEmbed: Boolean by config.boolean(true, "data.embed".toName()) diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Extruded.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Extruded.kt index 40e03512..d94062d8 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Extruded.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Extruded.kt @@ -2,7 +2,7 @@ package space.kscience.visionforge.solid import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import space.kscience.dataforge.meta.Config +import space.kscience.dataforge.meta.ObservableMeta import space.kscience.visionforge.* import kotlin.math.PI import kotlin.math.cos @@ -95,7 +95,7 @@ public class Extruded( public class ExtrudeBuilder( public var shape: List = emptyList(), public var layers: ArrayList = ArrayList(), - config: Config = Config() + config: ObservableMeta = ObservableMeta() ) : SimpleVisionPropertyContainer(config) { public fun shape(block: Shape2DBuilder.() -> Unit) { this.shape = Shape2DBuilder().apply(block).build() From eea617f418ff88584e1190e7408ab02247c31379 Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Thu, 5 Aug 2021 16:40:41 +0700 Subject: [PATCH 02/15] Use standard GA configuration --- .github/workflows/build.yml | 33 +++++++++++++++++++++++ .github/workflows/gradle.yml | 19 ------------- .github/workflows/pages.yml | 28 +++++++++++++++++++ gradle/wrapper/gradle-wrapper.jar | Bin 59203 -> 59536 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 2 +- 6 files changed, 63 insertions(+), 21 deletions(-) create mode 100644 .github/workflows/build.yml delete mode 100644 .github/workflows/gradle.yml create mode 100644 .github/workflows/pages.yml mode change 100644 => 100755 gradlew diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..412460ae --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,33 @@ +name: Gradle build + +on: + push: + branches: [ dev, master ] + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 40 + steps: + - uses: actions/checkout@v2 + - uses: DeLaGuardo/setup-graalvm@4.0 + with: + graalvm: 21.2.0 + java: java11 + arch: amd64 + - uses: actions/cache@v2 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('*.gradle.kts') }} + restore-keys: | + ${{ runner.os }}-gradle- + - uses: actions/cache@v2 + with: + path: ~/.konan + key: ${{ runner.os }}-gradle-${{ hashFiles('*.gradle.kts') }} + restore-keys: | + ${{ runner.os }}-gradle- + - run: ./gradlew build --build-cache --no-daemon --stacktrace diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml deleted file mode 100644 index 15ef5105..00000000 --- a/.github/workflows/gradle.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: Gradle build - -on: [push] - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v1 - - name: Set up JDK 11 - uses: actions/setup-java@v1 - with: - java-version: 11 - - name: Make gradlew executable - run: chmod +x ./gradlew - - name: Build with Gradle - run: ./gradlew build diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml new file mode 100644 index 00000000..134d3d48 --- /dev/null +++ b/.github/workflows/pages.yml @@ -0,0 +1,28 @@ +name: Dokka publication + +on: + push: + branches: [ master ] + +jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 40 + steps: + - uses: actions/checkout@v2 + - uses: DeLaGuardo/setup-graalvm@4.0 + with: + graalvm: 21.2.0 + java: java11 + arch: amd64 + - uses: actions/cache@v2 + with: + path: ~/.gradle/caches + key: ${{ runner.os }}-gradle-${{ hashFiles('*.gradle.kts') }} + restore-keys: | + ${{ runner.os }}-gradle- + - run: ./gradlew dokkaHtmlMultiModule --build-cache --no-daemon --no-parallel --stacktrace + - uses: JamesIves/github-pages-deploy-action@4.1.0 + with: + branch: gh-pages + folder: build/dokka/htmlMultiModule diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index e708b1c023ec8b20f512888fe07c5bd3ff77bb8f..7454180f2ae8848c63b8b4dea2cb829da983f2fa 100644 GIT binary patch delta 18435 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 B8dv}T delta 17845 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 X49Luk)baa85H9$5YCsTPT`SVRWMtMW diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f371643e..05679dc3 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 index 4f906e0c..744e882e --- a/gradlew +++ b/gradlew @@ -72,7 +72,7 @@ case "`uname`" in Darwin* ) darwin=true ;; - MINGW* ) + MSYS* | MINGW* ) msys=true ;; NONSTOP* ) From 8d21d3cd740ac9808f2292120eee405605b7ea5a Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 7 Aug 2021 11:27:57 +0300 Subject: [PATCH 03/15] Adjust property editors --- build.gradle.kts | 11 +- .../visionforge/gdml/GDMLVisionTest.kt | 4 +- .../visionforge/gdml/demo/GdmlFxDemoApp.kt | 11 +- .../kotlin/ru/mipt/npm/muon/monitor/Model.kt | 1 + demo/playground/build.gradle.kts | 1 + .../src/jvmMain/kotlin/markdownDemo.kt | 91 +++++++ .../src/jvmMain/kotlin/serverExtensions.kt | 2 +- .../src/jvmMain/kotlin/simpleCube.kt | 7 +- .../main/kotlin/ru/mipt/npm/sat/satServer.kt | 4 +- .../visionforge/solid/demo}/VisionLayout.kt | 3 +- .../kscience/visionforge/solid/demo/demo.kt | 5 +- .../visionforge/solid/demo/ThreeDemoGrid.kt | 1 - .../visionforge/solid/demo/FXDemoGrid.kt | 1 - .../visionforge/solid/demo/MetaEditorDemo.kt | 33 +-- docs/design.md | 2 +- docs/uml/Vision.puml | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- settings.gradle.kts | 2 +- ui/bootstrap/build.gradle.kts | 2 +- .../visionforge/bootstrap/outputConfig.kt | 5 +- .../visionforge/bootstrap/tabComponent.kt | 4 +- .../bootstrap/visionPropertyEditor.kt | 15 +- .../kscience/visionforge/react/MetaViewer.kt | 137 +++++------ .../visionforge/react/MultiSelectChooser.kt | 10 +- .../visionforge/react/PropertyEditor.kt | 155 ++++++------ .../visionforge/react/RangeValueChooser.kt | 4 +- .../visionforge/react/ThreeCanvasComponent.kt | 2 +- .../kscience/visionforge/react/VisionTree.kt | 2 +- .../visionforge/react/valueChooser.kt | 43 ++-- ui/ring/build.gradle.kts | 9 +- .../ThreeViewWithControls.kt | 17 +- .../ringPropertyEditor.kt | 15 +- .../ringThreeControls.kt | 7 +- .../space/kscience/visionforge/Colors.kt | 33 ++- .../kscience/visionforge/StyleReference.kt | 4 +- .../space/kscience/visionforge/StyleSheet.kt | 21 +- .../space/kscience/visionforge/Vision.kt | 88 ++++--- .../space/kscience/visionforge/VisionBase.kt | 76 +++--- .../kscience/visionforge/VisionChange.kt | 10 +- .../space/kscience/visionforge/VisionGroup.kt | 4 +- .../kscience/visionforge/VisionGroupBase.kt | 6 +- .../kscience/visionforge/VisionManager.kt | 15 +- .../kscience/visionforge/VisionProperties.kt | 38 +++ .../visionforge/VisionPropertyContainer.kt | 33 ++- .../visionforge/html/VisionTagConsumer.kt | 13 +- .../kotlin/space/kscience/visionforge/misc.kt | 24 +- .../kscience/visionforge/schemeDesctiptors.kt | 54 ++--- .../kscience/visionforge/visionDelegates.kt | 93 ++++++++ .../kscience/visionforge/visionDescriptor.kt | 64 ++--- .../kscience/visionforge/html/HtmlTagTest.kt | 2 +- .../kscience/visionforge/VisionClient.kt | 2 +- .../space/kscience/visionforge/FXPlugin.kt | 2 +- .../editor/ComboBoxValueChooser.kt | 4 +- .../visionforge/editor/ConfigEditor.kt | 189 --------------- .../kscience/visionforge/editor/FXMeta.kt | 223 ------------------ .../visionforge/editor/FXMetaModel.kt | 217 +++++++++++++++++ .../kscience/visionforge/editor/MetaViewer.kt | 41 ++-- .../visionforge/editor/MutableMetaEditor.kt | 193 +++++++++++++++ .../visionforge/editor/TextValueChooser.kt | 7 +- .../visionforge/editor/ValueCallback.kt | 4 +- .../visionforge/editor/ValueChooser.kt | 22 +- .../visionforge/editor/ValueChooserBase.kt | 16 +- ...torFragment.kt => VisionEditorFragment.kt} | 39 ++- ...tTreeFragment.kt => VisionTreeFragment.kt} | 11 +- .../kscience/visionforge/solid/FXMaterials.kt | 49 ++-- .../visionforge/solid/FXReferenceFactory.kt | 7 +- .../solid/VisualObjectFXBinding.kt | 38 ++- .../visionforge/gdml/GdmlTransformer.kt | 10 +- .../src/commonTest/kotlin/TestCubes.kt | 6 +- .../visionforge/gdml/TestConvertor.kt | 4 +- .../visionforge/markup/VisionOfMarkup.kt | 4 +- visionforge-plotly/build.gradle.kts | 2 +- .../visionforge/plotly/VisionOfPlotly.kt | 6 +- .../kscience/visionforge/plotly/plotlyJs.kt | 2 +- .../visionforge/three/server/VisionServer.kt | 21 +- .../visionforge/solid/ColorAccessor.kt | 16 +- .../kscience/visionforge/solid/Composite.kt | 2 +- .../kscience/visionforge/solid/Extruded.kt | 7 +- .../kscience/visionforge/solid/PolyLine.kt | 7 +- .../space/kscience/visionforge/solid/Solid.kt | 37 ++- .../kscience/visionforge/solid/SolidBase.kt | 8 +- .../kscience/visionforge/solid/SolidGroup.kt | 8 +- .../visionforge/solid/SolidMaterial.kt | 42 ++-- .../visionforge/solid/SolidReference.kt | 32 +-- .../kscience/visionforge/solid/geometry.kt | 10 +- .../visionforge/solid/specifications/Axes.kt | 6 +- .../solid/specifications/Camera.kt | 6 +- .../solid/specifications/Canvas3DOptions.kt | 39 ++- .../solid/transform/RemoveSingleChild.kt | 13 +- .../kscience/visionforge/solid/ConvexTest.kt | 7 +- .../visionforge/solid/DescriptorTest.kt | 3 +- .../visionforge/solid/PropertyTest.kt | 2 +- .../visionforge/solid/SerializationTest.kt | 15 +- .../visionforge/solid/VisionUpdateTest.kt | 15 +- .../solid/three/MeshThreeFactory.kt | 2 +- .../visionforge/solid/three/ThreeCanvas.kt | 10 +- .../solid/three/ThreeLineFactory.kt | 6 +- .../visionforge/solid/three/ThreeMaterials.kt | 42 ++-- .../visionforge/solid/three/ThreePlugin.kt | 6 +- .../solid/three/ThreeReferenceFactory.kt | 4 +- .../kscience/visionforge/solid/three/three.kt | 4 +- 101 files changed, 1410 insertions(+), 1246 deletions(-) create mode 100644 demo/playground/src/jvmMain/kotlin/markdownDemo.kt rename {visionforge-core/src/commonMain/kotlin/space/kscience/visionforge => demo/solid-showcase/src/commonMain/kotlin/space/kscience/visionforge/solid/demo}/VisionLayout.kt (69%) create mode 100644 visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionProperties.kt create mode 100644 visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/visionDelegates.kt delete mode 100644 visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/ConfigEditor.kt delete mode 100644 visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/FXMeta.kt create mode 100644 visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/FXMetaModel.kt create mode 100644 visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/MutableMetaEditor.kt rename visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/{VisualObjectEditorFragment.kt => VisionEditorFragment.kt} (57%) rename visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/{VisualObjectTreeFragment.kt => VisionTreeFragment.kt} (82%) diff --git a/build.gradle.kts b/build.gradle.kts index c7fbc2d8..22615a5d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,18 +1,13 @@ plugins { id("ru.mipt.npm.gradle.project") - - //Override kotlin version -// val kotlinVersion = "1.5.20-RC" -// kotlin("multiplatform") version(kotlinVersion) apply false -// kotlin("jvm") version(kotlinVersion) apply false -// kotlin("js") version(kotlinVersion) apply false } -val dataforgeVersion by extra("0.5.0-dev-2") +val dataforgeVersion by extra("0.5.0-dev-9") val fxVersion by extra("11") allprojects { repositories { + mavenLocal() mavenCentral() jcenter() maven("https://repo.kotlin.link") @@ -29,7 +24,7 @@ subprojects { } } -ksciencePublish{ +ksciencePublish { github("visionforge") space() sonatype() diff --git a/demo/gdml/src/commonTest/kotlin/space/kscience/visionforge/gdml/GDMLVisionTest.kt b/demo/gdml/src/commonTest/kotlin/space/kscience/visionforge/gdml/GDMLVisionTest.kt index 17bdc014..66332d9d 100644 --- a/demo/gdml/src/commonTest/kotlin/space/kscience/visionforge/gdml/GDMLVisionTest.kt +++ b/demo/gdml/src/commonTest/kotlin/space/kscience/visionforge/gdml/GDMLVisionTest.kt @@ -1,7 +1,7 @@ package space.kscience.visionforge.gdml import space.kscience.dataforge.meta.string -import space.kscience.dataforge.names.toName +import space.kscience.dataforge.names.Name import space.kscience.dataforge.values.asValue import space.kscience.gdml.GdmlShowCase import space.kscience.visionforge.setProperty @@ -24,7 +24,7 @@ class GDMLVisionTest { @Test fun testPrototypeProperty() { val vision = GdmlShowCase.cubes().toVision() - val child = vision["composite-000.segment-0".toName()] + val child = vision[Name.of("composite-000","segment-0")] assertNotNull(child) child.setProperty(SolidMaterial.MATERIAL_COLOR_KEY, "red".asValue()) assertEquals("red", child.getProperty(SolidMaterial.MATERIAL_COLOR_KEY).string) diff --git a/demo/gdml/src/jvmMain/kotlin/space/kscience/visionforge/gdml/demo/GdmlFxDemoApp.kt b/demo/gdml/src/jvmMain/kotlin/space/kscience/visionforge/gdml/demo/GdmlFxDemoApp.kt index 4fd1e2e7..d4cd85b0 100644 --- a/demo/gdml/src/jvmMain/kotlin/space/kscience/visionforge/gdml/demo/GdmlFxDemoApp.kt +++ b/demo/gdml/src/jvmMain/kotlin/space/kscience/visionforge/gdml/demo/GdmlFxDemoApp.kt @@ -8,8 +8,8 @@ import space.kscience.dataforge.context.fetch import space.kscience.gdml.GdmlShowCase import space.kscience.visionforge.VisionManager import space.kscience.visionforge.describedProperties -import space.kscience.visionforge.editor.VisualObjectEditorFragment -import space.kscience.visionforge.editor.VisualObjectTreeFragment +import space.kscience.visionforge.editor.VisionEditorFragment +import space.kscience.visionforge.editor.VisionTreeFragment import space.kscience.visionforge.gdml.toVision import space.kscience.visionforge.solid.FX3DPlugin import space.kscience.visionforge.solid.FXCanvas3D @@ -29,25 +29,24 @@ class GDMLView : View() { private val visionManager = context.fetch(VisionManager) private val canvas = FXCanvas3D(fx3d) - private val treeFragment = VisualObjectTreeFragment().apply { + private val treeFragment = VisionTreeFragment().apply { this.itemProperty.bind(canvas.rootObjectProperty) } - private val propertyEditor = VisualObjectEditorFragment { + private val propertyEditor = VisionEditorFragment { it.describedProperties }.apply { descriptorProperty.set(SolidMaterial.descriptor) itemProperty.bind(treeFragment.selectedProperty) } - override val root: Parent = borderpane { top { buttonbar { button("Load GDML/json") { action { val file = chooseFile("Select a GDML/json file", filters = fileNameFilter).firstOrNull() - if(file!= null) { + if (file != null) { runAsync { visionManager.readFile(file) as Solid } ui { diff --git a/demo/muon-monitor/src/commonMain/kotlin/ru/mipt/npm/muon/monitor/Model.kt b/demo/muon-monitor/src/commonMain/kotlin/ru/mipt/npm/muon/monitor/Model.kt index c485a1e3..87065723 100644 --- a/demo/muon-monitor/src/commonMain/kotlin/ru/mipt/npm/muon/monitor/Model.kt +++ b/demo/muon-monitor/src/commonMain/kotlin/ru/mipt/npm/muon/monitor/Model.kt @@ -6,6 +6,7 @@ import ru.mipt.npm.muon.monitor.Monitor.UPPER_LAYER_Z import space.kscience.visionforge.VisionManager import space.kscience.visionforge.removeAll import space.kscience.visionforge.root +import space.kscience.visionforge.setProperty import space.kscience.visionforge.solid.* import kotlin.math.PI diff --git a/demo/playground/build.gradle.kts b/demo/playground/build.gradle.kts index 7c99d6fa..73fd2aa7 100644 --- a/demo/playground/build.gradle.kts +++ b/demo/playground/build.gradle.kts @@ -67,6 +67,7 @@ kotlin { val jvmMain by getting{ dependencies { api(project(":visionforge-server")) + api(project(":visionforge-markdown")) api("ch.qos.logback:logback-classic:1.2.3") implementation("com.github.Ricky12Awesome:json-schema-serialization:0.6.6") } diff --git a/demo/playground/src/jvmMain/kotlin/markdownDemo.kt b/demo/playground/src/jvmMain/kotlin/markdownDemo.kt new file mode 100644 index 00000000..cb4b7cf0 --- /dev/null +++ b/demo/playground/src/jvmMain/kotlin/markdownDemo.kt @@ -0,0 +1,91 @@ +package space.kscience.visionforge.examples + +import kotlinx.html.div +import kotlinx.html.h1 +import space.kscience.dataforge.context.Context +import space.kscience.plotly.layout +import space.kscience.plotly.models.ScatterMode +import space.kscience.plotly.models.TextPosition +import space.kscience.plotly.scatter +import space.kscience.visionforge.html.ResourceLocation +import space.kscience.visionforge.markup.markdown +import space.kscience.visionforge.plotly.PlotlyPlugin +import space.kscience.visionforge.plotly.plotly +import space.kscience.visionforge.solid.* + +fun main() { + val context = Context { + plugin(Solids) + plugin(PlotlyPlugin) + } + + context.makeVisionFile(resourceLocation = ResourceLocation.SYSTEM) { + markdown { + //language=markdown + """ + # Section + + **TBD** + + ## Subsection + """.trimIndent() + } + + div { + h1 { +"Canvas" } + vision("canvas") { + solid { + box(100, 100, 100) + material { + emissiveColor("red") + } + } + } + } + + vision("plot") { + plotly { + scatter { + x(1, 2, 3, 4) + y(10, 15, 13, 17) + mode = ScatterMode.markers + name = "Team A" + text("A-1", "A-2", "A-3", "A-4", "A-5") + textposition = TextPosition.`top center` + textfont { + family = "Raleway, sans-serif" + } + marker { size = 12 } + } + + scatter { + x(2, 3, 4, 5) + y(10, 15, 13, 17) + mode = ScatterMode.lines + name = "Team B" + text("B-a", "B-b", "B-c", "B-d", "B-e") + textposition = TextPosition.`bottom center` + textfont { + family = "Times New Roman" + } + marker { size = 12 } + } + + layout { + title = "Data Labels Hover" + xaxis { + range(0.75..5.25) + } + legend { + y = 0.5 + font { + family = "Arial, sans-serif" + size = 20 + color("grey") + } + } + } + } + } + } +} \ No newline at end of file diff --git a/demo/playground/src/jvmMain/kotlin/serverExtensions.kt b/demo/playground/src/jvmMain/kotlin/serverExtensions.kt index b71b1873..e4111afc 100644 --- a/demo/playground/src/jvmMain/kotlin/serverExtensions.kt +++ b/demo/playground/src/jvmMain/kotlin/serverExtensions.kt @@ -26,7 +26,7 @@ public fun Context.makeVisionFile( content: VisionTagConsumer<*>.() -> Unit ): Unit { val actualPath = page(title, content = content).makeFile(path) { actualPath -> - mapOf("threeJs" to scriptHeader("js/visionforge-playground.js", resourceLocation, actualPath)) + mapOf("playground" to scriptHeader("js/visionforge-playground.js", resourceLocation, actualPath)) } if (show) Desktop.getDesktop().browse(actualPath.toFile().toURI()) } diff --git a/demo/playground/src/jvmMain/kotlin/simpleCube.kt b/demo/playground/src/jvmMain/kotlin/simpleCube.kt index e98d1ff1..f804228b 100644 --- a/demo/playground/src/jvmMain/kotlin/simpleCube.kt +++ b/demo/playground/src/jvmMain/kotlin/simpleCube.kt @@ -2,9 +2,7 @@ package space.kscience.visionforge.examples import space.kscience.dataforge.context.Context import space.kscience.visionforge.html.ResourceLocation -import space.kscience.visionforge.solid.Solids -import space.kscience.visionforge.solid.box -import space.kscience.visionforge.solid.solid +import space.kscience.visionforge.solid.* fun main() { val context = Context { @@ -15,6 +13,9 @@ fun main() { vision("canvas") { solid { box(100, 100, 100) + material { + emissiveColor("red") + } } } } diff --git a/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/satServer.kt b/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/satServer.kt index 6d80c691..8b286e07 100644 --- a/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/satServer.kt +++ b/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/satServer.kt @@ -8,7 +8,7 @@ import kotlinx.coroutines.launch import kotlinx.html.div import kotlinx.html.h1 import space.kscience.dataforge.context.Global -import space.kscience.dataforge.names.toName +import space.kscience.dataforge.names.Name import space.kscience.visionforge.solid.* import space.kscience.visionforge.three.server.* import space.kscience.visionforge.visionManager @@ -42,7 +42,7 @@ fun main() { val randomLayer = Random.nextInt(1, 11) val randomI = Random.nextInt(1, 4) val randomJ = Random.nextInt(1, 4) - val target = "layer[$randomLayer].segment[$randomI,$randomJ]".toName() + val target = Name.parse("layer[$randomLayer].segment[$randomI,$randomJ]") val targetVision = sat[target] as Solid targetVision.color("red") delay(1000) diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionLayout.kt b/demo/solid-showcase/src/commonMain/kotlin/space/kscience/visionforge/solid/demo/VisionLayout.kt similarity index 69% rename from visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionLayout.kt rename to demo/solid-showcase/src/commonMain/kotlin/space/kscience/visionforge/solid/demo/VisionLayout.kt index 1a0fddfc..016f2ecf 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionLayout.kt +++ b/demo/solid-showcase/src/commonMain/kotlin/space/kscience/visionforge/solid/demo/VisionLayout.kt @@ -1,7 +1,8 @@ -package space.kscience.visionforge +package space.kscience.visionforge.solid.demo import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.names.Name +import space.kscience.visionforge.Vision public interface VisionLayout { public fun render(name: Name, vision: V, meta: Meta = Meta.EMPTY) diff --git a/demo/solid-showcase/src/commonMain/kotlin/space/kscience/visionforge/solid/demo/demo.kt b/demo/solid-showcase/src/commonMain/kotlin/space/kscience/visionforge/solid/demo/demo.kt index 2385fef1..fcf94589 100644 --- a/demo/solid-showcase/src/commonMain/kotlin/space/kscience/visionforge/solid/demo/demo.kt +++ b/demo/solid-showcase/src/commonMain/kotlin/space/kscience/visionforge/solid/demo/demo.kt @@ -6,9 +6,8 @@ import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.invoke -import space.kscience.dataforge.names.toName +import space.kscience.dataforge.names.Name import space.kscience.visionforge.Colors -import space.kscience.visionforge.VisionLayout import space.kscience.visionforge.solid.* import space.kscience.visionforge.solid.specifications.Canvas3DOptions import space.kscience.visionforge.visible @@ -23,7 +22,7 @@ fun VisionLayout.demo(name: String, title: String = name, block: SolidGro "title" put title } val vision = SolidGroup(block) - render(name.toName(), vision) + render(Name.parse(name), vision) } val canvasOptions = Canvas3DOptions { diff --git a/demo/solid-showcase/src/jsMain/kotlin/space/kscience/visionforge/solid/demo/ThreeDemoGrid.kt b/demo/solid-showcase/src/jsMain/kotlin/space/kscience/visionforge/solid/demo/ThreeDemoGrid.kt index bef5404f..6dcfa36f 100644 --- a/demo/solid-showcase/src/jsMain/kotlin/space/kscience/visionforge/solid/demo/ThreeDemoGrid.kt +++ b/demo/solid-showcase/src/jsMain/kotlin/space/kscience/visionforge/solid/demo/ThreeDemoGrid.kt @@ -15,7 +15,6 @@ import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.get import space.kscience.dataforge.meta.string import space.kscience.dataforge.names.Name -import space.kscience.visionforge.VisionLayout import space.kscience.visionforge.solid.Solid import space.kscience.visionforge.solid.three.ThreeCanvas import space.kscience.visionforge.solid.three.ThreePlugin diff --git a/demo/solid-showcase/src/jvmMain/kotlin/space/kscience/visionforge/solid/demo/FXDemoGrid.kt b/demo/solid-showcase/src/jvmMain/kotlin/space/kscience/visionforge/solid/demo/FXDemoGrid.kt index 4e727bb6..ef03092a 100644 --- a/demo/solid-showcase/src/jvmMain/kotlin/space/kscience/visionforge/solid/demo/FXDemoGrid.kt +++ b/demo/solid-showcase/src/jvmMain/kotlin/space/kscience/visionforge/solid/demo/FXDemoGrid.kt @@ -7,7 +7,6 @@ import space.kscience.dataforge.context.Global import space.kscience.dataforge.context.fetch import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.names.Name -import space.kscience.visionforge.VisionLayout import space.kscience.visionforge.solid.FX3DPlugin import space.kscience.visionforge.solid.FXCanvas3D import space.kscience.visionforge.solid.Solid diff --git a/demo/solid-showcase/src/jvmMain/kotlin/space/kscience/visionforge/solid/demo/MetaEditorDemo.kt b/demo/solid-showcase/src/jvmMain/kotlin/space/kscience/visionforge/solid/demo/MetaEditorDemo.kt index fc231c38..b00ef055 100644 --- a/demo/solid-showcase/src/jvmMain/kotlin/space/kscience/visionforge/solid/demo/MetaEditorDemo.kt +++ b/demo/solid-showcase/src/jvmMain/kotlin/space/kscience/visionforge/solid/demo/MetaEditorDemo.kt @@ -2,12 +2,14 @@ package space.kscience.visionforge.demo import javafx.geometry.Orientation import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.meta.asConfig -import space.kscience.dataforge.meta.descriptors.NodeDescriptor +import space.kscience.dataforge.meta.MutableMeta +import space.kscience.dataforge.meta.descriptors.MetaDescriptor +import space.kscience.dataforge.meta.descriptors.node +import space.kscience.dataforge.meta.descriptors.value import space.kscience.dataforge.values.ValueType -import space.kscience.visionforge.editor.ConfigEditor -import space.kscience.visionforge.editor.FXMeta +import space.kscience.visionforge.editor.FXMetaModel import space.kscience.visionforge.editor.MetaViewer +import space.kscience.visionforge.editor.MutableMetaEditor import tornadofx.* @@ -15,7 +17,7 @@ class MetaEditorDemoApp : App(MetaEditorDemo::class) class MetaEditorDemo : View("Meta editor demo") { - val meta = Meta { + val meta = MutableMeta { "aNode" put { "innerNode" put { "innerValue" put true @@ -23,18 +25,16 @@ class MetaEditorDemo : View("Meta editor demo") { "b" put 223 "c" put "StringValue" } - }.asConfig() + } - val descriptor = NodeDescriptor { + val descriptor = MetaDescriptor { node("aNode") { info = "A root demo node" - value("b") { + value("b", ValueType.NUMBER) { info = "b number value" - type(ValueType.NUMBER) } node("otherNode") { - value("otherValue") { - type(ValueType.BOOLEAN) + value("otherValue", ValueType.BOOLEAN) { default(false) info = "default value" } @@ -46,12 +46,13 @@ class MetaEditorDemo : View("Meta editor demo") { } } - private val rootNode = FXMeta.root(meta, descriptor) + private val rootNode = FXMetaModel.root(meta, descriptor) - override val root = - splitpane(Orientation.HORIZONTAL, MetaViewer(rootNode).root, ConfigEditor( - rootNode - ).root) + override val root = splitpane( + Orientation.HORIZONTAL, + MetaViewer(rootNode as Meta).root, + MutableMetaEditor(rootNode as FXMetaModel).root + ) } fun main() { diff --git a/docs/design.md b/docs/design.md index 79014d73..c6b1b64b 100644 --- a/docs/design.md +++ b/docs/design.md @@ -1,5 +1,5 @@ ## Library design -The central point of the library design is the `Vision` interface. The `Vision` stores an optional reference to its parent and is able to store a number of mutable or read-only properties. Each property is represented by its `Name`, and a `MetaItem` value-tree, both following DataForge library specification (discussed in the [Appendix](appendix.md)). The `Vision` objects are organized in a tree using `VisionGroup` as nodes. `VisionGroup` additionally to all `Vision` properties holds a `children` container that holds named references to its direct children `Vision`s. Thus, `Vision`s form a doubly linked tree (a parent stores references to all its children and children store a reference to the parent). +The central point of the library design is the `Vision` interface. The `Vision` stores an optional reference to its parent and is able to store a number of mutable or read-only properties. Each property is represented by its `Name`, and a `Meta` value-tree, both following DataForge library specification (discussed in the [Appendix](appendix.md)). The `Vision` objects are organized in a tree using `VisionGroup` as nodes. `VisionGroup` additionally to all `Vision` properties holds a `children` container that holds named references to its direct children `Vision`s. Thus, `Vision`s form a doubly linked tree (a parent stores references to all its children and children store a reference to the parent). An important concept using in the VisionForge is the property layering mechanism. It means that if the property with a given name is not found in the `Vision` it is requested from, it could be requested from the parent `Vision`, form the style declaration, the prototype for the vision or any other place defined by the component author. For example, let's take a `color` attribute used in 3D visualization. When one draws a group of objects, he usually wants to make the color of all objects in the group to be defined by a single handle in the group common ancestor. So when the parent color changes, all children color must follow suite, but we also want to change children color individually without changing the parent. In this case two property layers are defined: diff --git a/docs/uml/Vision.puml b/docs/uml/Vision.puml index 2028b097..079ca82c 100644 --- a/docs/uml/Vision.puml +++ b/docs/uml/Vision.puml @@ -3,7 +3,7 @@ interface Vision{ val parent: Vision? - fun getProperty(name): MetaItem? + fun getProperty(name): Meta? fun setProperty(name, value) } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f371643e..05679dc3 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/settings.gradle.kts b/settings.gradle.kts index 70f3606a..d88c9b81 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,6 +1,6 @@ pluginManagement { - val toolsVersion = "0.10.0" + val toolsVersion = "0.10.2" repositories { maven("https://repo.kotlin.link") diff --git a/ui/bootstrap/build.gradle.kts b/ui/bootstrap/build.gradle.kts index b1b0588e..af4ec727 100644 --- a/ui/bootstrap/build.gradle.kts +++ b/ui/bootstrap/build.gradle.kts @@ -11,5 +11,5 @@ dependencies { implementation(npm("file-saver", "2.0.2")) implementation(npm("bootstrap","4.6.0")) implementation(npm("jquery","3.5.1")) - implementation(npm("popper.js","1.16.1")) + implementation(npm("@popperjs/core","2.9.3")) } \ No newline at end of file diff --git a/ui/bootstrap/src/main/kotlin/space/kscience/visionforge/bootstrap/outputConfig.kt b/ui/bootstrap/src/main/kotlin/space/kscience/visionforge/bootstrap/outputConfig.kt index 3423baf8..f16985c5 100644 --- a/ui/bootstrap/src/main/kotlin/space/kscience/visionforge/bootstrap/outputConfig.kt +++ b/ui/bootstrap/src/main/kotlin/space/kscience/visionforge/bootstrap/outputConfig.kt @@ -12,6 +12,7 @@ import org.w3c.files.BlobPropertyBag import react.* import react.dom.attrs import react.dom.button +import space.kscience.dataforge.meta.descriptors.defaultNode import space.kscience.dataforge.meta.withDefault import space.kscience.visionforge.Vision import space.kscience.visionforge.react.flexColumn @@ -43,7 +44,7 @@ public external interface CanvasControlsProps : RProps { public var vision: Vision? } -public val CanvasControls: FunctionalComponent = functionalComponent("CanvasControls") { props -> +public val CanvasControls: FunctionComponent = functionalComponent("CanvasControls") { props -> flexColumn { flexRow { css { @@ -66,7 +67,7 @@ public val CanvasControls: FunctionalComponent = functional } propertyEditor( ownProperties = props.canvasOptions, - allProperties = props.canvasOptions.withDefault(Canvas3DOptions.descriptor.defaultMeta), + allProperties = props.canvasOptions.meta.withDefault(Canvas3DOptions.descriptor.defaultNode), descriptor = Canvas3DOptions.descriptor, expanded = false ) diff --git a/ui/bootstrap/src/main/kotlin/space/kscience/visionforge/bootstrap/tabComponent.kt b/ui/bootstrap/src/main/kotlin/space/kscience/visionforge/bootstrap/tabComponent.kt index 5ff0d5e8..987dbc0e 100644 --- a/ui/bootstrap/src/main/kotlin/space/kscience/visionforge/bootstrap/tabComponent.kt +++ b/ui/bootstrap/src/main/kotlin/space/kscience/visionforge/bootstrap/tabComponent.kt @@ -18,7 +18,7 @@ public external class TabProps : RProps { } @JsExport -public val Tab: FunctionalComponent = functionalComponent { props -> +public val Tab: FunctionComponent = functionalComponent { props -> props.children() } @@ -27,7 +27,7 @@ public external class TabPaneProps : RProps { } @JsExport -public val TabPane: FunctionalComponent = functionalComponent("TabPane") { props -> +public val TabPane: FunctionComponent = functionalComponent("TabPane") { props -> var activeTab: String? by useState(props.activeTab) val children: Array = Children.map(props.children) { diff --git a/ui/bootstrap/src/main/kotlin/space/kscience/visionforge/bootstrap/visionPropertyEditor.kt b/ui/bootstrap/src/main/kotlin/space/kscience/visionforge/bootstrap/visionPropertyEditor.kt index 7f28473e..9c2206f1 100644 --- a/ui/bootstrap/src/main/kotlin/space/kscience/visionforge/bootstrap/visionPropertyEditor.kt +++ b/ui/bootstrap/src/main/kotlin/space/kscience/visionforge/bootstrap/visionPropertyEditor.kt @@ -3,22 +3,25 @@ package space.kscience.visionforge.bootstrap import org.w3c.dom.Element import react.RBuilder import react.dom.render -import space.kscience.dataforge.meta.descriptors.NodeDescriptor -import space.kscience.visionforge.* +import space.kscience.dataforge.meta.descriptors.MetaDescriptor +import space.kscience.visionforge.Vision +import space.kscience.visionforge.getStyle +import space.kscience.visionforge.meta import space.kscience.visionforge.react.metaViewer import space.kscience.visionforge.react.propertyEditor import space.kscience.visionforge.solid.SolidReference +import space.kscience.visionforge.styles public fun RBuilder.visionPropertyEditor( vision: Vision, - descriptor: NodeDescriptor? = vision.descriptor, + descriptor: MetaDescriptor? = vision.descriptor, key: Any? = null, ) { card("Properties") { propertyEditor( - ownProperties = vision.ownProperties, - allProperties = vision.allProperties(), + ownProperties = vision.meta(false,false,false), + allProperties = vision.meta(), updateFlow = vision.propertyChanges, descriptor = descriptor, key = key @@ -47,7 +50,7 @@ public fun RBuilder.visionPropertyEditor( public fun Element.visionPropertyEditor( item: Vision, - descriptor: NodeDescriptor? = item.descriptor, + descriptor: MetaDescriptor? = item.descriptor, ): Unit = render(this) { visionPropertyEditor(item, descriptor = descriptor) } \ No newline at end of file diff --git a/ui/react/src/main/kotlin/space/kscience/visionforge/react/MetaViewer.kt b/ui/react/src/main/kotlin/space/kscience/visionforge/react/MetaViewer.kt index a3972612..288251e0 100644 --- a/ui/react/src/main/kotlin/space/kscience/visionforge/react/MetaViewer.kt +++ b/ui/react/src/main/kotlin/space/kscience/visionforge/react/MetaViewer.kt @@ -8,12 +8,11 @@ import react.* import react.dom.a import react.dom.attrs import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.meta.MetaItemNode -import space.kscience.dataforge.meta.MetaItemValue -import space.kscience.dataforge.meta.descriptors.ItemDescriptor -import space.kscience.dataforge.meta.descriptors.NodeDescriptor +import space.kscience.dataforge.meta.descriptors.MetaDescriptor +import space.kscience.dataforge.meta.descriptors.defaultNode import space.kscience.dataforge.meta.descriptors.get import space.kscience.dataforge.meta.get +import space.kscience.dataforge.meta.isLeaf import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.NameToken import space.kscience.dataforge.names.lastOrNull @@ -41,18 +40,19 @@ public external interface MetaViewerProps : RProps { /** * Root descriptor */ - public var descriptor: NodeDescriptor? + public var descriptor: MetaDescriptor? } -private val MetaViewerItem: FunctionalComponent = functionalComponent("MetaViewerItem") { props -> +private val MetaViewerItem: FunctionComponent = functionalComponent("MetaViewerItem") { props -> metaViewerItem(props) } private fun RBuilder.metaViewerItem(props: MetaViewerProps) { var expanded: Boolean by useState { true } val item = props.root[props.name] - val descriptorItem: ItemDescriptor? = props.descriptor?.get(props.name) - val actualItem = item ?: descriptorItem?.defaultValue + val descriptorItem: MetaDescriptor? = props.descriptor?.get(props.name) + val actualValue = item?.value ?: descriptorItem?.defaultValue + val actualMeta = item ?: descriptorItem?.defaultNode val token = props.name.lastOrNull()?.toString() ?: props.rootName ?: "" @@ -60,90 +60,75 @@ private fun RBuilder.metaViewerItem(props: MetaViewerProps) { expanded = !expanded } - when (actualItem) { - is MetaItemNode -> { - flexRow { + flexRow { + css { + alignItems = Align.center + } + if (actualMeta?.isLeaf == false) { + styledSpan { css { - alignItems = Align.center - } - styledSpan { - css { - +TreeStyles.treeCaret - if (expanded) { - +TreeStyles.treeCaredDown - } - } - attrs { - onClickFunction = expanderClick + +TreeStyles.treeCaret + if (expanded) { + +TreeStyles.treeCaredDown } } - styledSpan { - css { - +TreeStyles.treeLabel - if (item == null) { - +TreeStyles.treeLabelInactive - } - } - +token - } - } - if (expanded) { - flexColumn { - css { - +TreeStyles.tree - } - val keys = buildSet { - (descriptorItem as? NodeDescriptor)?.items?.keys?.forEach { - add(NameToken(it)) - } - actualItem.node.items.keys.let { addAll(it) } - } - - keys.filter { !it.body.startsWith("@") }.forEach { token -> - styledDiv { - css { - +TreeStyles.treeItem - } - child(MetaViewerItem) { - attrs { - this.key = props.name.toString() - this.root = props.root - this.name = props.name + token - this.descriptor = props.descriptor - } - } - //configEditor(props.root, props.name + token, props.descriptor, props.default) - } - } + attrs { + onClickFunction = expanderClick } } } - is MetaItemValue -> { - flexRow { - css { - alignItems = Align.center + + styledSpan { + css { + +TreeStyles.treeLabel + if (item == null) { + +TreeStyles.treeLabelInactive } - styledSpan { + } + +token + } + styledDiv { + a { + +actualValue.toString() + } + } + } + if (expanded) { + flexColumn { + css { + +TreeStyles.tree + } + val keys = buildSet { + descriptorItem?.children?.keys?.forEach { + add(NameToken(it)) + } + actualMeta!!.items.keys.let { addAll(it) } + } + + keys.filter { !it.body.startsWith("@") }.forEach { token -> + styledDiv { css { - +TreeStyles.treeLabel - if (item == null) { - +TreeStyles.treeLabelInactive + +TreeStyles.treeItem + } + child(MetaViewerItem) { + attrs { + this.key = props.name.toString() + this.root = props.root + this.name = props.name + token + this.descriptor = props.descriptor } } - +token - } - styledDiv { - a { - +actualItem.value.toString() - } + //configEditor(props.root, props.name + token, props.descriptor, props.default) } } } } + + } @JsExport -public val MetaViewer: FunctionalComponent = +public val MetaViewer: FunctionComponent = functionalComponent("MetaViewer") { props -> child(MetaViewerItem) { attrs { @@ -155,7 +140,7 @@ public val MetaViewer: FunctionalComponent = } } -public fun RBuilder.metaViewer(meta: Meta, descriptor: NodeDescriptor? = null, key: Any? = null) { +public fun RBuilder.metaViewer(meta: Meta, descriptor: MetaDescriptor? = null, key: Any? = null) { child(MetaViewer) { attrs { this.key = key?.toString() ?: "" diff --git a/ui/react/src/main/kotlin/space/kscience/visionforge/react/MultiSelectChooser.kt b/ui/react/src/main/kotlin/space/kscience/visionforge/react/MultiSelectChooser.kt index a363fcd5..9f698df7 100644 --- a/ui/react/src/main/kotlin/space/kscience/visionforge/react/MultiSelectChooser.kt +++ b/ui/react/src/main/kotlin/space/kscience/visionforge/react/MultiSelectChooser.kt @@ -5,23 +5,23 @@ import org.w3c.dom.HTMLOptionElement import org.w3c.dom.HTMLSelectElement import org.w3c.dom.asList import org.w3c.dom.events.Event -import react.FunctionalComponent +import react.FunctionComponent import react.dom.attrs import react.dom.option import react.dom.select import react.functionalComponent import react.useState -import space.kscience.dataforge.meta.value +import space.kscience.dataforge.meta.descriptors.allowedValues import space.kscience.dataforge.values.asValue import space.kscience.dataforge.values.string @JsExport -public val MultiSelectChooser: FunctionalComponent = +public val MultiSelectChooser: FunctionComponent = functionalComponent("MultiSelectChooser") { props -> - var selectedItems by useState { props.item.value?.list ?: emptyList() } + var selectedItems by useState { props.item?.value?.list ?: emptyList() } 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() } props.valueChanged?.invoke(newSelected.asValue()) selectedItems = newSelected diff --git a/ui/react/src/main/kotlin/space/kscience/visionforge/react/PropertyEditor.kt b/ui/react/src/main/kotlin/space/kscience/visionforge/react/PropertyEditor.kt index 6e41555b..cdb1bf30 100644 --- a/ui/react/src/main/kotlin/space/kscience/visionforge/react/PropertyEditor.kt +++ b/ui/react/src/main/kotlin/space/kscience/visionforge/react/PropertyEditor.kt @@ -18,14 +18,10 @@ import react.* import react.dom.attrs import react.dom.render import space.kscience.dataforge.meta.* -import space.kscience.dataforge.meta.descriptors.ItemDescriptor -import space.kscience.dataforge.meta.descriptors.NodeDescriptor -import space.kscience.dataforge.meta.descriptors.ValueDescriptor +import space.kscience.dataforge.meta.descriptors.MetaDescriptor +import space.kscience.dataforge.meta.descriptors.ValueRequirement import space.kscience.dataforge.meta.descriptors.get -import space.kscience.dataforge.names.Name -import space.kscience.dataforge.names.NameToken -import space.kscience.dataforge.names.lastOrNull -import space.kscience.dataforge.names.plus +import space.kscience.dataforge.names.* import space.kscience.dataforge.values.Value import space.kscience.visionforge.hidden import styled.css @@ -38,12 +34,12 @@ public external interface PropertyEditorProps : RProps { /** * Root config object - always non null */ - public var ownProperties: MutableItemProvider + public var ownProperties: MutableMetaProvider /** * Provide default item (greyed out if used) */ - public var allProperties: ItemProvider? + public var allProperties: MetaProvider? /** * Full path to the displayed node in [ownProperties]. Could be empty @@ -53,7 +49,7 @@ public external interface PropertyEditorProps : RProps { /** * Root descriptor */ - public var descriptor: NodeDescriptor? + public var descriptor: MetaDescriptor? /** * A coroutine scope for updates @@ -71,21 +67,21 @@ public external interface PropertyEditorProps : RProps { public var expanded: Boolean? } -private val PropertyEditorItem: FunctionalComponent = +private val PropertyEditorItem: FunctionComponent = functionalComponent("ConfigEditorItem") { props -> propertyEditorItem(props) } private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) { var expanded: Boolean by useState { props.expanded ?: true } - val descriptorItem: ItemDescriptor? = props.descriptor?.get(props.name) - var ownProperty: MetaItem? by useState { props.ownProperties.getItem(props.name) } - val actualItem: MetaItem? = props.allProperties?.getItem(props.name) + val descriptor: MetaDescriptor? = props.descriptor?.get(props.name) + var ownProperty: Meta? by useState { props.ownProperties.getMeta(props.name) } + val actualMeta = props.allProperties?.getMeta(props.name) val token = props.name.lastOrNull()?.toString() ?: "Properties" fun update() { - ownProperty = props.ownProperties.getItem(props.name) + ownProperty = props.ownProperties.getMeta(props.name) } if (props.updateFlow != null) { @@ -109,7 +105,7 @@ private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) { if (it == null) { props.ownProperties.remove(props.name) } else { - props.ownProperties[props.name] = it + props.ownProperties.setValue(props.name, it) } update() } @@ -119,19 +115,20 @@ private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) { update() } - if (actualItem is MetaItemNode) { - val keys = buildSet { - (descriptorItem as? NodeDescriptor)?.items?.filterNot { - it.key.startsWith("@") || it.value.hidden - }?.forEach { - add(NameToken(it.key)) - } - ownProperty?.node?.items?.keys?.filterNot { it.body.startsWith("@") }?.let { addAll(it) } + val keys = buildSet { + descriptor?.children?.filterNot { + it.key.startsWith("@") || it.value.hidden + }?.forEach { + add(NameToken(it.key)) } - // Do not show nodes without visible children - if (keys.isEmpty()) return + ownProperty?.items?.keys?.filterNot { it.body.startsWith("@") }?.let { addAll(it) } + } - flexRow { + flexRow { + css { + alignItems = Align.center + } + if(keys.isNotEmpty()) { styledSpan { css { +TreeStyles.treeCaret @@ -143,55 +140,17 @@ private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) { onClickFunction = expanderClick } } - styledSpan { - css { - +TreeStyles.treeLabel - if (ownProperty == null) { - +TreeStyles.treeLabelInactive - } - } - +token - } } - if (expanded) { - flexColumn { - css { - +TreeStyles.tree - } - keys.forEach { token -> - styledDiv { - css { - +TreeStyles.treeItem - } - child(PropertyEditorItem) { - attrs { - this.key = props.name.toString() - this.ownProperties = props.ownProperties - this.allProperties = props.allProperties - this.name = props.name + token - this.descriptor = props.descriptor - } - } - //configEditor(props.root, props.name + token, props.descriptor, props.default) - } - } - } - } - } else { - flexRow { + styledSpan { css { - alignItems = Align.center - } - styledSpan { - css { - +TreeStyles.treeLabel - if (ownProperty == null) { - +TreeStyles.treeLabelInactive - } + +TreeStyles.treeLabel + if (ownProperty == null) { + +TreeStyles.treeLabelInactive } - +token } - + +token + } + if(!props.name.isEmpty() && descriptor?.valueRequirement != ValueRequirement.ABSENT) { styledDiv { css { //+TreeStyles.resizeableInput @@ -200,8 +159,8 @@ private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) { } valueChooser( props.name, - actualItem, - descriptorItem as? ValueDescriptor, + actualMeta, + descriptor, valueChanged ) } @@ -232,14 +191,38 @@ private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) { } } } - } } + if (expanded) { + flexColumn { + css { + +TreeStyles.tree + } + keys.forEach { token -> + styledDiv { + css { + +TreeStyles.treeItem + } + child(PropertyEditorItem) { + attrs { + this.key = props.name.toString() + this.ownProperties = props.ownProperties + this.allProperties = props.allProperties + this.name = props.name + token + this.descriptor = props.descriptor + } + } + //configEditor(props.root, props.name + token, props.descriptor, props.default) + } + } + } + } + } @JsExport -public val PropertyEditor: FunctionalComponent = functionalComponent("PropertyEditor") { props -> +public val PropertyEditor: FunctionComponent = functionalComponent("PropertyEditor") { props -> child(PropertyEditorItem) { attrs { this.key = "" @@ -254,10 +237,10 @@ public val PropertyEditor: FunctionalComponent = functional } public fun RBuilder.propertyEditor( - ownProperties: MutableItemProvider, - allProperties: ItemProvider? = ownProperties, + ownProperties: MutableMetaProvider, + allProperties: MetaProvider? = ownProperties, updateFlow: Flow? = null, - descriptor: NodeDescriptor? = null, + descriptor: MetaDescriptor? = null, scope: CoroutineScope? = null, key: Any? = null, expanded: Boolean? = null @@ -276,8 +259,8 @@ public fun RBuilder.propertyEditor( } @OptIn(ExperimentalCoroutinesApi::class) -private fun Config.flowUpdates(): Flow = callbackFlow { - onChange(this) { name, _, _ -> +private fun ObservableMutableMeta.flowUpdates(): Flow = callbackFlow { + onChange(this) { name -> launch { send(name) } @@ -289,16 +272,16 @@ private fun Config.flowUpdates(): Flow = callbackFlow { public fun RBuilder.configEditor( - config: Config, - default: ItemProvider? = null, - descriptor: NodeDescriptor? = null, + config: ObservableMutableMeta, + default: MetaProvider? = null, + descriptor: MetaDescriptor? = null, key: Any? = null, scope: CoroutineScope? = null, ): Unit = propertyEditor(config, default, config.flowUpdates(), descriptor, scope, key = key) public fun Element.configEditor( - config: Config, - descriptor: NodeDescriptor? = null, + config: ObservableMutableMeta, + descriptor: MetaDescriptor? = null, default: Meta? = null, key: Any? = null, scope: CoroutineScope? = null, diff --git a/ui/react/src/main/kotlin/space/kscience/visionforge/react/RangeValueChooser.kt b/ui/react/src/main/kotlin/space/kscience/visionforge/react/RangeValueChooser.kt index 2b11143b..28513416 100644 --- a/ui/react/src/main/kotlin/space/kscience/visionforge/react/RangeValueChooser.kt +++ b/ui/react/src/main/kotlin/space/kscience/visionforge/react/RangeValueChooser.kt @@ -6,7 +6,7 @@ import kotlinx.html.InputType import kotlinx.html.js.onChangeFunction import org.w3c.dom.HTMLInputElement import org.w3c.dom.events.Event -import react.FunctionalComponent +import react.FunctionComponent import react.dom.attrs import react.functionalComponent import react.useState @@ -18,7 +18,7 @@ import styled.css import styled.styledInput @JsExport -public val RangeValueChooser: FunctionalComponent = +public val RangeValueChooser: FunctionComponent = functionalComponent("RangeValueChooser") { props -> var innerValue by useState(props.item.double) var rangeDisabled: Boolean by useState(props.item == null) diff --git a/ui/react/src/main/kotlin/space/kscience/visionforge/react/ThreeCanvasComponent.kt b/ui/react/src/main/kotlin/space/kscience/visionforge/react/ThreeCanvasComponent.kt index 6dd563de..41034773 100644 --- a/ui/react/src/main/kotlin/space/kscience/visionforge/react/ThreeCanvasComponent.kt +++ b/ui/react/src/main/kotlin/space/kscience/visionforge/react/ThreeCanvasComponent.kt @@ -21,7 +21,7 @@ public external interface ThreeCanvasProps : RProps { public var selected: Name? } -public val ThreeCanvasComponent: FunctionalComponent = functionalComponent( +public val ThreeCanvasComponent: FunctionComponent = functionalComponent( "ThreeCanvasComponent" ) { props -> val elementRef = useRef(null) diff --git a/ui/react/src/main/kotlin/space/kscience/visionforge/react/VisionTree.kt b/ui/react/src/main/kotlin/space/kscience/visionforge/react/VisionTree.kt index bcb7e84a..6be045d6 100644 --- a/ui/react/src/main/kotlin/space/kscience/visionforge/react/VisionTree.kt +++ b/ui/react/src/main/kotlin/space/kscience/visionforge/react/VisionTree.kt @@ -107,7 +107,7 @@ private fun RBuilder.visionTree(props: ObjectTreeProps): Unit { } @JsExport -public val ObjectTree: FunctionalComponent = functionalComponent("ObjectTree") { props -> +public val ObjectTree: FunctionComponent = functionalComponent("ObjectTree") { props -> visionTree(props) } diff --git a/ui/react/src/main/kotlin/space/kscience/visionforge/react/valueChooser.kt b/ui/react/src/main/kotlin/space/kscience/visionforge/react/valueChooser.kt index 2147b191..2cfcc976 100644 --- a/ui/react/src/main/kotlin/space/kscience/visionforge/react/valueChooser.kt +++ b/ui/react/src/main/kotlin/space/kscience/visionforge/react/valueChooser.kt @@ -13,8 +13,12 @@ import org.w3c.dom.events.Event import react.* import react.dom.attrs import react.dom.option -import space.kscience.dataforge.meta.* -import space.kscience.dataforge.meta.descriptors.ValueDescriptor +import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.boolean +import space.kscience.dataforge.meta.descriptors.MetaDescriptor +import space.kscience.dataforge.meta.descriptors.allowedValues +import space.kscience.dataforge.meta.get +import space.kscience.dataforge.meta.string import space.kscience.dataforge.names.Name import space.kscience.dataforge.values.* import space.kscience.visionforge.Colors @@ -24,14 +28,15 @@ import styled.styledInput import styled.styledSelect public external interface ValueChooserProps : RProps { - public var item: MetaItem? - public var descriptor: ValueDescriptor? + public var item: Meta? + public var descriptor: MetaDescriptor? + //public var nullable: Boolean? public var valueChanged: ((Value?) -> Unit)? } @JsExport -public val StringValueChooser: FunctionalComponent = +public val StringValueChooser: FunctionComponent = functionalComponent("StringValueChooser") { props -> var value by useState(props.item.string ?: "") val keyDown: (Event) -> Unit = { event -> @@ -46,7 +51,7 @@ public val StringValueChooser: FunctionalComponent = value = (it.target as HTMLInputElement).value } styledInput(type = InputType.text) { - css{ + css { width = 100.pct } attrs { @@ -58,14 +63,14 @@ public val StringValueChooser: FunctionalComponent = } @JsExport -public val BooleanValueChooser: FunctionalComponent = +public val BooleanValueChooser: FunctionComponent = functionalComponent("BooleanValueChooser") { props -> val handleChange: (Event) -> Unit = { val newValue = (it.target as HTMLInputElement).checked props.valueChanged?.invoke(newValue.asValue()) } styledInput(type = InputType.checkBox) { - css{ + css { width = 100.pct } attrs { @@ -77,7 +82,7 @@ public val BooleanValueChooser: FunctionalComponent = } @JsExport -public val NumberValueChooser: FunctionalComponent = +public val NumberValueChooser: FunctionComponent = functionalComponent("NumberValueChooser") { props -> var innerValue by useState(props.item.string ?: "") val keyDown: (Event) -> Unit = { event -> @@ -95,7 +100,7 @@ public val NumberValueChooser: FunctionalComponent = innerValue = (it.target as HTMLInputElement).value } styledInput(type = InputType.number) { - css{ + css { width = 100.pct } attrs { @@ -116,7 +121,7 @@ public val NumberValueChooser: FunctionalComponent = } @JsExport -public val ComboValueChooser: FunctionalComponent = +public val ComboValueChooser: FunctionComponent = functionalComponent("ComboValueChooser") { props -> var selected by useState(props.item.string ?: "") val handleChange: (Event) -> Unit = { @@ -124,7 +129,7 @@ public val ComboValueChooser: FunctionalComponent = props.valueChanged?.invoke(selected.asValue()) } styledSelect { - css{ + css { width = 100.pct } props.descriptor?.allowedValues?.forEach { @@ -141,10 +146,10 @@ public val ComboValueChooser: FunctionalComponent = } @JsExport -public val ColorValueChooser: FunctionalComponent = +public val ColorValueChooser: FunctionComponent = functionalComponent("ColorValueChooser") { props -> var value by useState( - props.item.value?.let { value -> + props.item?.value?.let { value -> if (value.type == ValueType.NUMBER) Colors.rgbToString(value.int) else value.string } ?: "#000000" @@ -154,7 +159,7 @@ public val ColorValueChooser: FunctionalComponent = props.valueChanged?.invoke(value.asValue()) } styledInput(type = InputType.color) { - css{ + css { width = 100.pct margin(0.px) } @@ -166,11 +171,11 @@ public val ColorValueChooser: FunctionalComponent = } @JsExport -public val ValueChooser: FunctionalComponent = functionalComponent("ValueChooser") { props -> +public val ValueChooser: FunctionComponent = functionalComponent("ValueChooser") { props -> val rawInput by useState(false) val descriptor = props.descriptor - val type = descriptor?.type?.firstOrNull() + val type = descriptor?.valueTypes?.firstOrNull() when { rawInput -> child(StringValueChooser, props) @@ -187,8 +192,8 @@ public val ValueChooser: FunctionalComponent = functionalComp internal fun RBuilder.valueChooser( name: Name, - item: MetaItem?, - descriptor: ValueDescriptor? = null, + item: Meta?, + descriptor: MetaDescriptor? = null, callback: (Value?) -> Unit, ) { child(ValueChooser) { diff --git a/ui/ring/build.gradle.kts b/ui/ring/build.gradle.kts index 84d33b43..84b4112f 100644 --- a/ui/ring/build.gradle.kts +++ b/ui/ring/build.gradle.kts @@ -17,14 +17,13 @@ kotlin{ dependencies{ api(project(":ui:react")) - //TODO replace by kotlin-wrappers - api("ru.mipt.npm:ring-ui:0.1.0") + //api("ru.mipt.npm:ring-ui:0.1.0") + api("org.jetbrains.kotlin-wrappers:kotlin-ring-ui") implementation(npm("@jetbrains/icons", "3.14.1")) implementation(npm("@jetbrains/ring-ui", "4.0.7")) implementation(npm("core-js","3.12.1")) implementation(npm("file-saver", "2.0.2")) - compileOnly(npm("url-loader","4.1.1")) - compileOnly(npm("postcss-loader","5.2.0")) - compileOnly(npm("source-map-loader","2.0.1")) +// compileOnly(npm("url-loader","4.1.1")) +// compileOnly(npm("postcss-loader","5.2.0")) } \ No newline at end of file diff --git a/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ThreeViewWithControls.kt b/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ThreeViewWithControls.kt index 7b427fba..d0aa2ed8 100644 --- a/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ThreeViewWithControls.kt +++ b/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ThreeViewWithControls.kt @@ -14,8 +14,7 @@ import space.kscience.dataforge.names.NameToken import space.kscience.dataforge.names.isEmpty import space.kscience.dataforge.names.length import space.kscience.visionforge.VisionGroup -import space.kscience.visionforge.allProperties -import space.kscience.visionforge.ownProperties +import space.kscience.visionforge.meta import space.kscience.visionforge.react.ThreeCanvasComponent import space.kscience.visionforge.react.flexColumn import space.kscience.visionforge.react.flexRow @@ -70,7 +69,7 @@ public fun RBuilder.nameCrumbs(name: Name?, link: (Name) -> Unit): ReactElement } @JsExport -public val ThreeCanvasWithControls: FunctionalComponent = +public val ThreeCanvasWithControls: FunctionComponent = functionalComponent("ThreeViewWithControls") { props -> var selected by useState { props.selected } @@ -120,23 +119,23 @@ public val ThreeCanvasWithControls: FunctionalComponent styledDiv { - css{ + css { position = Position.absolute top = 5.px right = 5.px width = 450.px } - Island{ - IslandHeader{ + Island { + IslandHeader { attrs { border = true } nameCrumbs(selected) { selected = it } } - IslandContent{ + IslandContent { propertyEditor( - ownProperties = vision.ownProperties, - allProperties = vision.allProperties(), + ownProperties = vision.meta(false, false, false), + allProperties = vision.meta(), updateFlow = vision.propertyChanges, descriptor = vision.descriptor, key = selected diff --git a/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ringPropertyEditor.kt b/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ringPropertyEditor.kt index e33dfb2b..f7783898 100644 --- a/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ringPropertyEditor.kt +++ b/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ringPropertyEditor.kt @@ -7,16 +7,19 @@ import react.dom.render import ringui.Island import ringui.SmartTabs import ringui.Tab -import space.kscience.dataforge.meta.descriptors.NodeDescriptor -import space.kscience.visionforge.* +import space.kscience.dataforge.meta.descriptors.MetaDescriptor +import space.kscience.visionforge.Vision +import space.kscience.visionforge.getStyle +import space.kscience.visionforge.meta import space.kscience.visionforge.react.flexColumn import space.kscience.visionforge.react.metaViewer import space.kscience.visionforge.react.propertyEditor import space.kscience.visionforge.solid.SolidReference +import space.kscience.visionforge.styles public fun RBuilder.ringPropertyEditor( vision: Vision, - descriptor: NodeDescriptor? = vision.descriptor, + descriptor: MetaDescriptor? = vision.descriptor, key: Any? = null, ) { val styles = if (vision is SolidReference) { @@ -28,8 +31,8 @@ public fun RBuilder.ringPropertyEditor( flexColumn { Island("Properties") { propertyEditor( - ownProperties = vision.ownProperties, - allProperties = vision.allProperties(), + ownProperties = vision.meta(false,false,false), + allProperties = vision.meta(), updateFlow = vision.propertyChanges, descriptor = descriptor, key = key @@ -69,7 +72,7 @@ public fun RBuilder.ringPropertyEditor( public fun Element.ringPropertyEditor( item: Vision, - descriptor: NodeDescriptor? = item.descriptor, + descriptor: MetaDescriptor? = item.descriptor, ): Unit = render(this) { ringPropertyEditor(item, descriptor = descriptor) } \ No newline at end of file diff --git a/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ringThreeControls.kt b/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ringThreeControls.kt index b0fc188c..1d9626ed 100644 --- a/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ringThreeControls.kt +++ b/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ringThreeControls.kt @@ -15,6 +15,7 @@ import react.dom.button import ringui.Island import ringui.SmartTabs import ringui.Tab +import space.kscience.dataforge.meta.descriptors.defaultNode import space.kscience.dataforge.meta.withDefault import space.kscience.dataforge.names.Name import space.kscience.visionforge.Vision @@ -49,7 +50,7 @@ internal external interface CanvasControlsProps : RProps { public var vision: Vision? } -internal val CanvasControls: FunctionalComponent = functionalComponent("CanvasControls") { props -> +internal val CanvasControls: FunctionComponent = functionalComponent("CanvasControls") { props -> flexColumn { flexRow { css { @@ -73,7 +74,7 @@ internal val CanvasControls: FunctionalComponent = function } propertyEditor( ownProperties = props.options, - allProperties = props.options.withDefault(Canvas3DOptions.descriptor.defaultMeta), + allProperties = props.options.meta.withDefault(Canvas3DOptions.descriptor.defaultNode), descriptor = Canvas3DOptions.descriptor, expanded = false ) @@ -91,7 +92,7 @@ public external interface ThreeControlsProps : RProps { } @JsExport -public val ThreeControls: FunctionalComponent = functionalComponent { props -> +public val ThreeControls: FunctionComponent = functionalComponent { props -> SmartTabs("Tree") { props.vision?.let { Tab("Tree") { diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/Colors.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/Colors.kt index 3f228272..58be399e 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/Colors.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/Colors.kt @@ -1,6 +1,8 @@ package space.kscience.visionforge -import space.kscience.dataforge.meta.* +import space.kscience.dataforge.meta.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 @@ -190,25 +192,18 @@ public object Colors { /** * Convert color represented as Meta to string of format #rrggbb */ - fun fromMeta(item: MetaItem): String { - return when (item) { - is MetaItemNode -> { - val node = item.node - rgbToString( - node[RED_KEY].number?.toByte()?.toUByte() ?: 0u, - node[GREEN_KEY].number?.toByte()?.toUByte() ?: 0u, - node[BLUE_KEY].number?.toByte()?.toUByte() ?: 0u - ) - } - is MetaItemValue -> { - if (item.value.type == ValueType.NUMBER) { - rgbToString(item.value.int) - } else { - item.value.string - } - } + fun fromMeta(meta: Meta): String = meta.value?.let { value -> + //if value is present, use it + if (value.type == ValueType.NUMBER) { + rgbToString(value.int) + } else { + value.string } - } + } ?: rgbToString( + meta[RED_KEY].number?.toByte()?.toUByte() ?: 0u, + meta[GREEN_KEY].number?.toByte()?.toUByte() ?: 0u, + meta[BLUE_KEY].number?.toByte()?.toUByte() ?: 0u + ) /** * Convert Int color to string of format #rrggbb diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/StyleReference.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/StyleReference.kt index 961e937a..d5dfac6e 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/StyleReference.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/StyleReference.kt @@ -1,7 +1,7 @@ package space.kscience.visionforge import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.meta.MetaBuilder +import space.kscience.dataforge.meta.MutableMeta import space.kscience.dataforge.meta.Scheme import space.kscience.dataforge.meta.Specification import kotlin.properties.ReadOnlyProperty @@ -27,7 +27,7 @@ public fun Vision.useStyle(reference: StyleReference) { @VisionBuilder public fun VisionGroup.style( styleKey: String? = null, - builder: MetaBuilder.() -> Unit, + builder: MutableMeta.() -> Unit, ): ReadOnlyProperty = ReadOnlyProperty { _, property -> val styleName = styleKey ?: property.name styleSheet.define(styleName, Meta(builder)) diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/StyleSheet.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/StyleSheet.kt index 655cb0f3..e2869df8 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/StyleSheet.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/StyleSheet.kt @@ -5,6 +5,7 @@ import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.NameToken import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.plus +import space.kscience.dataforge.values.asValue import kotlin.jvm.JvmInline /** @@ -13,9 +14,9 @@ import kotlin.jvm.JvmInline @JvmInline public value class StyleSheet(private val owner: VisionGroup) { - private val styleNode get() = owner.ownProperties[STYLESHEET_KEY].node + private val styleNode: Meta? get() = owner.getOwnProperty(STYLESHEET_KEY) - public val items: Map? get() = styleNode?.items?.mapValues { it.value.node ?: Meta.EMPTY } + public val items: Map? get() = styleNode?.items public operator fun get(key: String): Meta? = owner.getStyle(key) @@ -23,7 +24,7 @@ public value class StyleSheet(private val owner: VisionGroup) { * Define a style without notifying owner */ public fun define(key: String, style: Meta?) { - owner.setProperty(STYLESHEET_KEY + key, style) + owner.setPropertyNode(STYLESHEET_KEY + key, style) } /** @@ -40,7 +41,7 @@ public value class StyleSheet(private val owner: VisionGroup) { /** * Create and set a style */ - public operator fun set(key: String, builder: MetaBuilder.() -> Unit) { + public operator fun set(key: String, builder: MutableMeta.() -> Unit) { val newStyle = get(key)?.toMutableMeta()?.apply(builder) ?: Meta(builder) set(key, newStyle.seal()) } @@ -70,9 +71,9 @@ internal fun Vision.styleChanged(key: String, oldStyle: Meta?, newStyle: Meta?) * List of names of styles applied to this object. Order matters. Not inherited. */ public var Vision.styles: List - get() = ownProperties[Vision.STYLE_KEY]?.stringList ?: emptyList() + get() = getOwnProperty(Vision.STYLE_KEY)?.stringList ?: emptyList() set(value) { - setProperty(Vision.STYLE_KEY, value) + setPropertyValue(Vision.STYLE_KEY, value.map { it.asValue() }.asValue()) } /** @@ -85,7 +86,7 @@ public val VisionGroup.styleSheet: StyleSheet get() = StyleSheet(this) * Add style name to the list of styles to be resolved later. The style with given name does not necessary exist at the moment. */ public fun Vision.useStyle(name: String) { - styles = (ownProperties[Vision.STYLE_KEY]?.stringList ?: emptyList()) + name + styles = (getOwnProperty(Vision.STYLE_KEY)?.stringList ?: emptyList()) + name } @@ -93,13 +94,13 @@ public fun Vision.useStyle(name: String) { * Find a style with given name for given [Vision]. The style is not necessary applied to this [Vision]. */ public tailrec fun Vision.getStyle(name: String): Meta? = - ownProperties[StyleSheet.STYLESHEET_KEY + name].node ?: parent?.getStyle(name) + getOwnProperty(StyleSheet.STYLESHEET_KEY + name) ?: parent?.getStyle(name) /** * Resolve an item in all style layers */ -public fun Vision.getStyleItems(name: Name): List = styles.mapNotNull { - getStyle(it)[name] +public fun Vision.getStyleItems(name: Name): List = styles.mapNotNull { + getStyle(it)?.get(name) } diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/Vision.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/Vision.kt index 07f063ca..daa854a5 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/Vision.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/Vision.kt @@ -4,14 +4,15 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import space.kscience.dataforge.meta.* +import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.MutableMeta import space.kscience.dataforge.meta.descriptors.Described -import space.kscience.dataforge.meta.descriptors.NodeDescriptor -import space.kscience.dataforge.meta.descriptors.get +import space.kscience.dataforge.meta.descriptors.MetaDescriptor +import space.kscience.dataforge.meta.update import space.kscience.dataforge.misc.Type import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.asName -import space.kscience.dataforge.names.toName +import space.kscience.dataforge.values.Value import space.kscience.visionforge.Vision.Companion.TYPE import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext @@ -45,13 +46,13 @@ public interface Vision : Described, CoroutineScope { inherit: Boolean = false, includeStyles: Boolean = true, includeDefaults: Boolean = true, - ): MetaItem? + ): Meta? /** * Get an intrinsic property of this Vision excluding any inheritance or defaults. In most cases should be the same as * `getProperty(name, false, false, false`. */ - public fun getOwnProperty(name: Name): MetaItem? = getProperty( + public fun getOwnProperty(name: Name): Meta? = getProperty( name, inherit = false, includeStyles = false, @@ -59,9 +60,14 @@ public interface Vision : Described, CoroutineScope { ) /** - * Set the property value + * Replace the property node. If [node] is null remove node and its descendants */ - public fun setProperty(name: Name, item: MetaItem?, notify: Boolean = true) + public fun setPropertyNode(name: Name, node: Meta?, notify: Boolean = true) + + /** + * Set a value of specific property node + */ + public fun setPropertyValue(name: Name, value: Value?, notify: Boolean = true) /** * Flow of property invalidation events. It does not contain property values after invalidation since it is not clear @@ -77,9 +83,9 @@ public interface Vision : Described, CoroutineScope { /** * Update this vision using a dif represented by [VisionChange]. */ - public fun update(change: VisionChange) + public fun change(change: VisionChange) - override val descriptor: NodeDescriptor? + override val descriptor: MetaDescriptor? public companion object { public const val TYPE: String = "vision" @@ -89,11 +95,6 @@ public interface Vision : Described, CoroutineScope { } } -/** - * Root property node - */ -public val Vision.meta: Meta get() = ownProperties[Name.EMPTY]?.node ?: Meta.EMPTY - /** * Subscribe on property updates. The subscription is bound to the given [scope] and canceled when the scope is canceled */ @@ -105,31 +106,26 @@ public fun Vision.onPropertyChange(scope: CoroutineScope, callback: suspend (Nam /** * Own properties, excluding inheritance, styles and descriptor */ -public val Vision.ownProperties: MutableItemProvider - get() = object : MutableItemProvider { - override fun getItem(name: Name): MetaItem? = getOwnProperty(name) - override fun setItem(name: Name, item: MetaItem?): Unit = setProperty(name, item) - } - -/** - * Convenient accessor for all properties of a vision. - * @param inherit - inherit property value from the parent by default. If null, inheritance is inferred from descriptor - */ -public fun Vision.allProperties( - inherit: Boolean? = null, - includeStyles: Boolean? = null, +public fun Vision.meta( + inherit: Boolean = false, + includeStyles: Boolean = true, includeDefaults: Boolean = true, -): MutableItemProvider = object : MutableItemProvider { - override fun getItem(name: Name): MetaItem? = getProperty( - name, - inherit = inherit ?: (descriptor?.get(name)?.inherited == true), - includeStyles = includeStyles ?: (descriptor?.get(name)?.usesStyles != false), - includeDefaults = includeDefaults - ) +): MutableMeta = VisionProperties(this, Name.EMPTY, inherit, includeStyles, includeDefaults) - override fun setItem(name: Name, item: MetaItem?): Unit = setProperty(name, item) +public fun Vision.configure(target: Name = Name.EMPTY, block: MutableMeta.() -> Unit): Unit { + VisionProperties(this, target).apply(block) } +public fun Vision.configure(meta: Meta) { + configure(Name.EMPTY) { + update(meta) + } +} + +public fun Vision.configure(block: MutableMeta.() -> Unit): Unit = configure(Meta(block)) + +public fun Vision.getOwnProperty(key: String): Meta? = getOwnProperty(Name.parse(key)) + /** * Get [Vision] property using key as a String */ @@ -138,18 +134,20 @@ public fun Vision.getProperty( inherit: Boolean = false, includeStyles: Boolean = true, includeDefaults: Boolean = true, -): MetaItem? = getProperty(key.toName(), inherit, includeStyles, includeDefaults) +): Meta? = getProperty(Name.parse(key), inherit, includeStyles, includeDefaults) + /** - * A convenience method to pair [getProperty] + * A convenience method to set property node or value. If Item is null, then node is removed, not a value */ -public fun Vision.setProperty(key: Name, item: Any?) { - setProperty(key, MetaItem.of(item)) +public fun Vision.setProperty(name: Name, item: Any?) { + when (item) { + null -> setPropertyNode(name, null) + is Meta -> setPropertyNode(name, item) + is Value -> setPropertyValue(name, item) + } } -/** - * A convenience method to pair [getProperty] - */ -public fun Vision.setProperty(key: String, item: Any?) { - setProperty(key.toName(), MetaItem.of(item)) +public fun Vision.setPropertyNode(key: String, item: Any?) { + setProperty(Name.parse(key), item) } diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionBase.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionBase.kt index 4dcffbca..46e5f62a 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionBase.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionBase.kt @@ -7,21 +7,18 @@ import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.Transient import space.kscience.dataforge.meta.* -import space.kscience.dataforge.meta.descriptors.NodeDescriptor +import space.kscience.dataforge.meta.descriptors.MetaDescriptor +import space.kscience.dataforge.meta.descriptors.defaultNode +import space.kscience.dataforge.meta.descriptors.value import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.plus -import space.kscience.dataforge.values.Null +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 PropertyListener( - val owner: Any? = null, - val action: (name: Name) -> Unit, -) - /** * A full base implementation for a [Vision] * @param properties Object own properties excluding styles and inheritance @@ -29,14 +26,17 @@ internal data class PropertyListener( @Serializable @SerialName("vision") public open class VisionBase( - override @Transient var parent: VisionGroup? = null, - protected var properties: MutableItemProvider? = null + @Transient override var parent: VisionGroup? = null, + @Serializable(MutableMetaSerializer::class) + protected var properties: MutableMeta? = null ) : Vision { + //protected val observableProperties: ObservableMutableMeta by lazy { properties.asObservable() } + @Synchronized - protected fun getOrCreateProperties(): MutableItemProvider { + protected fun getOrCreateProperties(): MutableMeta { if (properties == null) { - val newProperties = MetaBuilder() + val newProperties = MutableMeta() properties = newProperties } return properties!! @@ -45,18 +45,14 @@ public open class VisionBase( /** * A fast accessor method to get own property (no inheritance or styles */ - override fun getOwnProperty(name: Name): MetaItem? = if (name == Name.EMPTY) { - properties?.rootItem - } else { - properties?.getItem(name) - } + override fun getOwnProperty(name: Name): Meta? = properties?.getMeta(name) override fun getProperty( name: Name, inherit: Boolean, includeStyles: Boolean, includeDefaults: Boolean, - ): MetaItem? = if (!inherit && !includeStyles && !includeDefaults) { + ): Meta? = if (!inherit && !includeStyles && !includeDefaults) { getOwnProperty(name) } else { buildList { @@ -68,22 +64,32 @@ public open class VisionBase( add(parent?.getProperty(name, inherit, includeStyles, includeDefaults)) } if (includeDefaults) { - add(descriptor?.defaultMeta?.get(name)) + add(descriptor?.defaultNode?.get(name)) } }.merge() } - override fun setProperty(name: Name, item: MetaItem?, notify: Boolean) { - val oldItem = properties?.getItem(name) - if(oldItem!= item) { - getOrCreateProperties().setItem(name, item) + override fun setPropertyNode(name: Name, node: Meta?, notify: Boolean) { + val oldItem = properties?.get(name) + if (oldItem != node) { + getOrCreateProperties().setMeta(name, node) if (notify) { invalidateProperty(name) } } } - override val descriptor: NodeDescriptor? get() = null + override fun setPropertyValue(name: Name, value: Value?, notify: Boolean) { + val oldItem = properties?.get(name)?.value + if (oldItem != value) { + getOrCreateProperties()[name] = value + if (notify) { + invalidateProperty(name) + } + } + } + + override val descriptor: MetaDescriptor? get() = null private suspend fun updateStyles(names: List) { names.mapNotNull { getStyle(it) }.asSequence() @@ -111,31 +117,23 @@ public open class VisionBase( } } - override fun update(change: VisionChange) { + override fun change(change: VisionChange) { change.properties?.let { - updateProperties(Name.EMPTY, it.asMetaItem()) + updateProperties(Name.EMPTY, it) } } public companion object { - public val descriptor: NodeDescriptor = NodeDescriptor { - value(STYLE_KEY) { - type(ValueType.STRING) + public val descriptor: MetaDescriptor = MetaDescriptor { + value(STYLE_KEY, ValueType.STRING) { multiple = true } } - public fun Vision.updateProperties(at: Name, item: MetaItem) { - when (item) { - is MetaItemValue -> { - if (item.value == Null) { - setProperty(at, null) - } else - setProperty(at, item) - } - is MetaItemNode -> item.node.items.forEach { (token, childItem) -> - updateProperties(at + token, childItem) - } + public fun Vision.updateProperties(at: Name, item: Meta) { + setPropertyValue(at, item.value) + item.items.forEach { (token, item) -> + updateProperties(at + token, item) } } diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionChange.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionChange.kt index aa50001a..e538a89c 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionChange.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionChange.kt @@ -21,7 +21,7 @@ public class VisionChangeBuilder : VisionContainerBuilder { private var reset: Boolean = false private var vision: Vision? = null - private val propertyChange = MetaBuilder() + private val propertyChange = MutableMeta() private val children: HashMap = HashMap() public fun isEmpty(): Boolean = propertyChange.isEmpty() && propertyChange.isEmpty() && children.isEmpty() @@ -30,17 +30,17 @@ public class VisionChangeBuilder : VisionContainerBuilder { private fun getOrPutChild(visionName: Name): VisionChangeBuilder = children.getOrPut(visionName) { VisionChangeBuilder() } - public fun propertyChanged(visionName: Name, propertyName: Name, item: MetaItem?) { + public fun propertyChanged(visionName: Name, propertyName: Name, item: Meta?) { if (visionName == Name.EMPTY) { //Write property removal as [Null] - propertyChange[propertyName] = (item ?: Null.asMetaItem()) + propertyChange[propertyName] = (item ?: Meta(Null)) } else { getOrPutChild(visionName).propertyChanged(Name.EMPTY, propertyName, item) } } override fun set(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 { vision = child reset = vision == null @@ -90,7 +90,7 @@ private fun CoroutineScope.collectChange( //Collect properties change source.onPropertyChange(this) { propertyName -> - val newItem = source.ownProperties[propertyName] + val newItem = source.getOwnProperty(propertyName) collector().propertyChanged(name, propertyName, newItem) } diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroup.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroup.kt index 6b35d971..c7c47901 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroup.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroup.kt @@ -75,12 +75,12 @@ public interface MutableVisionGroup : VisionGroup, VisionContainerBuilder } -public operator fun VisionContainer.get(str: String): V? = get(str.toName()) +public operator fun VisionContainer.get(str: String): V? = get(Name.parse(str)) public operator fun VisionContainerBuilder.set(token: NameToken, child: V?): Unit = set(token.asName(), child) public operator fun VisionContainerBuilder.set(key: String?, child: V?): Unit = - set(key?.toName(), child) + set(key?.let(Name::parse), child) public fun MutableVisionGroup.removeAll(): Unit = children.keys.map { it.asName() }.forEach { this[it] = null } \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroupBase.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroupBase.kt index 933b55cd..aab27bc5 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroupBase.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroupBase.kt @@ -131,15 +131,15 @@ public open class VisionGroupBase( } } - override fun update(change: VisionChange) { + override fun change(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) + else -> get(name)?.change(change) } } - super.update(change) + super.change(change) } } diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionManager.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionManager.kt index 4f01e839..3f9bfaa8 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionManager.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionManager.kt @@ -8,12 +8,10 @@ import kotlinx.serialization.modules.polymorphic import kotlinx.serialization.modules.subclass import space.kscience.dataforge.context.* import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.meta.descriptors.NodeDescriptor -import space.kscience.dataforge.meta.node +import space.kscience.dataforge.meta.descriptors.MetaDescriptor import space.kscience.dataforge.meta.toJson -import space.kscience.dataforge.meta.toMetaItem +import space.kscience.dataforge.meta.toMeta import space.kscience.dataforge.names.Name -import space.kscience.dataforge.names.toName import kotlin.reflect.KClass public class VisionManager(meta: Meta) : AbstractPlugin(meta) { @@ -48,12 +46,11 @@ public class VisionManager(meta: Meta) : AbstractPlugin(meta) { jsonFormat.encodeToJsonElement(visionSerializer, vision) //TODO remove double transformation with dedicated Meta serial format - public fun decodeFromMeta(meta: Meta, descriptor: NodeDescriptor? = null): Vision = + public fun decodeFromMeta(meta: Meta, descriptor: MetaDescriptor? = null): Vision = decodeFromJson(meta.toJson(descriptor)) - public fun encodeToMeta(vision: Vision, descriptor: NodeDescriptor? = null): Meta = - encodeToJsonElement(vision).toMetaItem(descriptor).node - ?: error("Expected node, but value found. Check your serializer!") + public fun encodeToMeta(vision: Vision, descriptor: MetaDescriptor? = null): Meta = + encodeToJsonElement(vision).toMeta(descriptor) public companion object : PluginFactory { override val tag: PluginTag = PluginTag(name = "vision", group = PluginTag.DATAFORGE_GROUP) @@ -89,7 +86,7 @@ public abstract class VisionPlugin(meta: Meta = Meta.EMPTY) : AbstractPlugin(met protected abstract val visionSerializersModule: SerializersModule override fun content(target: String): Map = when (target) { - VisionManager.VISION_SERIALIZER_MODULE_TARGET -> mapOf(tag.toString().toName() to visionSerializersModule) + VisionManager.VISION_SERIALIZER_MODULE_TARGET -> mapOf(Name.parse(tag.toString()) to visionSerializersModule) else -> super.content(target) } } diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionProperties.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionProperties.kt new file mode 100644 index 00000000..363ad729 --- /dev/null +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionProperties.kt @@ -0,0 +1,38 @@ +package space.kscience.visionforge + +import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.MutableMeta +import space.kscience.dataforge.names.Name +import space.kscience.dataforge.names.NameToken +import space.kscience.dataforge.names.plus +import space.kscience.dataforge.values.Value + +internal class VisionProperties( + val vision: Vision, + val rootName: Name, + val inherit: Boolean = false, + val includeStyles: Boolean = true, + val includeDefaults: Boolean = true, +) : MutableMeta { + + override val items: Map + get() = vision.getProperty(rootName, inherit, includeStyles, includeDefaults)?.items?.mapValues { + VisionProperties(vision, rootName + it.key, inherit, includeStyles, includeDefaults) + } ?: emptyMap() + + override var value: Value? + get() = vision.getProperty(rootName, inherit, includeStyles, includeDefaults)?.value + set(value) { + vision.setPropertyValue(rootName, value) + } + + override fun getOrCreate(name: Name): MutableMeta = VisionProperties(vision, rootName + name) + + override fun setMeta(name: Name, node: Meta?) { + vision.setPropertyNode(rootName + name, node) + } + + override fun equals(other: Any?): Boolean = Meta.equals(this, other as? Meta) + override fun hashCode(): Int = Meta.hashCode(this) + override fun toString(): String = Meta.toString(this) +} \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionPropertyContainer.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionPropertyContainer.kt index 3710175c..f6f822b7 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionPropertyContainer.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionPropertyContainer.kt @@ -1,32 +1,49 @@ package space.kscience.visionforge -import space.kscience.dataforge.meta.* +import space.kscience.dataforge.meta.Configurable +import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.ObservableMutableMeta +import space.kscience.dataforge.meta.get import space.kscience.dataforge.names.Name +import space.kscience.dataforge.values.Value /** * Property containers are used to create a symmetric behaviors for vision properties and style builders */ -public interface VisionPropertyContainer { +public interface VisionPropertyContainer { public fun getProperty( name: Name, inherit: Boolean = false, includeStyles: Boolean = true, includeDefaults: Boolean = true, - ): MetaItem? + ): Meta? - public fun setProperty(name: Name, item: MetaItem?, notify: Boolean = true) + /** + * Replace the property node. If [node] is null remove node and its descendants + */ + public fun setPropertyNode(name: Name, node: Meta?, notify: Boolean = true) + + /** + * Set a value of specific property node + */ + public fun setPropertyValue(name: Name, value: Value?, notify: Boolean = true) } -public open class SimpleVisionPropertyContainer(protected val config: ObservableMeta): VisionPropertyContainer{ +public open class SimpleVisionPropertyContainer( + override val meta: ObservableMutableMeta, +) : VisionPropertyContainer, Configurable { override fun getProperty( name: Name, inherit: Boolean, includeStyles: Boolean, includeDefaults: Boolean - ): MetaItem? = config[name] + ): Meta? = meta[name] - override fun setProperty(name: Name, item: MetaItem?, notify: Boolean) { - config[name] = item + override fun setPropertyNode(name: Name, node: Meta?, notify: Boolean) { + this.meta.setMeta(name, node) } + override fun setPropertyValue(name: Name, value: Value?, notify: Boolean) { + this.meta.setValue(name, value) + } } \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/VisionTagConsumer.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/VisionTagConsumer.kt index 287f32bc..a0b64d8b 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/VisionTagConsumer.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/VisionTagConsumer.kt @@ -2,12 +2,13 @@ package space.kscience.visionforge.html import kotlinx.html.* import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.meta.MetaBuilder import space.kscience.dataforge.meta.MetaSerializer +import space.kscience.dataforge.meta.MutableMeta import space.kscience.dataforge.meta.isEmpty import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.names.Name -import space.kscience.dataforge.names.toName +import space.kscience.dataforge.names.NameToken +import space.kscience.dataforge.names.asName import space.kscience.visionforge.Vision import space.kscience.visionforge.VisionManager import kotlin.collections.set @@ -25,7 +26,7 @@ public class VisionOutput @PublishedApi internal constructor(public val manager: //TODO expose a way to define required plugins. - public inline fun meta(block: MetaBuilder.() -> Unit) { + public inline fun meta(block: MutableMeta.() -> Unit) { this.meta = Meta(block) } } @@ -36,7 +37,7 @@ public class VisionOutput @PublishedApi internal constructor(public val manager: @VisionDSL public abstract class VisionTagConsumer( private val root: TagConsumer, - public val manager:VisionManager, + public val manager: VisionManager, private val idPrefix: String? = null, ) : TagConsumer by root { @@ -94,11 +95,11 @@ public abstract class VisionTagConsumer( public inline fun TagConsumer.vision( name: String = DEFAULT_VISION_NAME, visionProvider: VisionOutput.() -> Vision, - ): T = vision(name.toName(), visionProvider) + ): T = vision(Name.parse(name), visionProvider) public fun TagConsumer.vision( vision: Vision, - ): T = vision("vision[${vision.hashCode()}]".toName(), vision) + ): T = vision(NameToken("vision", vision.hashCode().toString()).asName(), vision) /** * Process the resulting object produced by [TagConsumer] diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/misc.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/misc.kt index f471b234..900f6f1c 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/misc.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/misc.kt @@ -1,18 +1,20 @@ package space.kscience.visionforge -import space.kscience.dataforge.meta.* +import space.kscience.dataforge.meta.Laminate +import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.boolean +import space.kscience.dataforge.meta.isLeaf import space.kscience.dataforge.values.asValue @DslMarker public annotation class VisionBuilder -public fun List.merge(): MetaItem? = when (val first = firstOrNull { it != null }) { - null -> null - is MetaItemValue -> first //fast search for first entry if it is value - is MetaItemNode -> { - //merge nodes if first encountered node is meta - val laminate: Laminate = Laminate(mapNotNull { it.node }) - MetaItemNode(laminate) +public fun List.merge(): Meta? { + val first = firstOrNull { it != null } + return when { + first == null -> null + first.isLeaf -> first //fast search for first entry if it is value + else -> Laminate(filterNotNull()) //merge nodes if first encountered node is meta } } @@ -21,8 +23,4 @@ public fun List.merge(): MetaItem? = when (val first = firstOrNull { */ public var Vision.visible: Boolean? get() = getProperty(Vision.VISIBLE_KEY).boolean - set(value) = setProperty(Vision.VISIBLE_KEY, value?.asValue()) - -public fun Vision.configure(meta: Meta?): Unit = update(VisionChange(properties = meta)) - -public fun Vision.configure(block: MetaBuilder.() -> Unit): Unit = configure(Meta(block)) \ No newline at end of file + set(value) = setPropertyValue(Vision.VISIBLE_KEY, value?.asValue()) \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/schemeDesctiptors.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/schemeDesctiptors.kt index 4187843f..403312ea 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/schemeDesctiptors.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/schemeDesctiptors.kt @@ -2,11 +2,11 @@ package space.kscience.visionforge import space.kscience.dataforge.meta.Scheme import space.kscience.dataforge.meta.SchemeSpec -import space.kscience.dataforge.meta.asObservable -import space.kscience.dataforge.meta.descriptors.NodeDescriptor -import space.kscience.dataforge.meta.descriptors.NodeDescriptorBuilder -import space.kscience.dataforge.meta.descriptors.ValueDescriptorBuilder -import space.kscience.dataforge.meta.toMutableMeta +import space.kscience.dataforge.meta.descriptors.Described +import space.kscience.dataforge.meta.descriptors.MetaDescriptorBuilder +import space.kscience.dataforge.meta.descriptors.item +import space.kscience.dataforge.meta.descriptors.value +import space.kscience.dataforge.names.Name import space.kscience.dataforge.values.ValueType import kotlin.reflect.KProperty1 import kotlin.reflect.typeOf @@ -14,55 +14,53 @@ import kotlin.reflect.typeOf /** * TODO to be moved into the core */ -public inline fun NodeDescriptorBuilder.value( +public inline fun MetaDescriptorBuilder.value( property: KProperty1, - noinline block: ValueDescriptorBuilder.() -> Unit = {}, + noinline block: MetaDescriptorBuilder.() -> Unit = {}, ) { when (typeOf()) { typeOf(), typeOf(), typeOf(), typeOf(), typeOf(), typeOf() -> - value(property.name) { - type(ValueType.NUMBER) + value(property.name, ValueType.NUMBER) { block() } typeOf(), typeOf(), typeOf(), typeOf(), typeOf(), typeOf() -> - value(property.name) { - type(ValueType.NUMBER) + value(property.name, ValueType.NUMBER) { block() } - typeOf() -> value(property.name) { - type(ValueType.BOOLEAN) + typeOf() -> value(property.name, ValueType.BOOLEAN) { block() } typeOf>(), typeOf>(), typeOf>(), typeOf>(), typeOf>(), typeOf>(), typeOf(), typeOf(), typeOf(), typeOf(), typeOf(), - -> value(property.name) { - type(ValueType.NUMBER) + -> value(property.name, ValueType.NUMBER) { multiple = true block() } - typeOf() -> value(property.name) { - type(ValueType.STRING) + typeOf() -> value(property.name, ValueType.STRING) { block() } - typeOf>(), typeOf>() -> value(property.name) { - type(ValueType.STRING) + typeOf>(), typeOf>() -> value(property.name, ValueType.STRING) { multiple = true block() } - else -> value(property.name, block) + else -> item(property.name, block) } } -public fun NodeDescriptor.copy(block: NodeDescriptorBuilder.() -> Unit = {}): NodeDescriptor { - return NodeDescriptorBuilder(toMeta().toMutableMeta().asObservable()).apply(block) +public fun MetaDescriptorBuilder.item( + key: String, + described: Described, + block: MetaDescriptorBuilder.() -> Unit = {}, +) { + described.descriptor?.let { + item(Name.parse(key), it, block) + } } -public inline fun NodeDescriptorBuilder.scheme( +public inline fun MetaDescriptorBuilder.scheme( property: KProperty1, spec: SchemeSpec, - noinline block: NodeDescriptorBuilder.() -> Unit = {}, + noinline block: MetaDescriptorBuilder.() -> Unit = {}, ) { - spec.descriptor?.let { descriptor -> - item(property.name, descriptor.copy(block)) - } -} \ No newline at end of file + item(property.name, spec, block) +} diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/visionDelegates.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/visionDelegates.kt new file mode 100644 index 00000000..524363d7 --- /dev/null +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/visionDelegates.kt @@ -0,0 +1,93 @@ +package space.kscience.visionforge + +import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.transformations.MetaConverter +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 = object : ReadWriteProperty { + 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?) { + setPropertyNode(name ?: Name.parse(property.name), value) + } +} + +public fun Vision.propertyNode( + converter: MetaConverter, + name: Name? = null, + inherit: Boolean = false, + includeStyles: Boolean = true, + includeDefaults: Boolean = true, +): ReadWriteProperty = object : ReadWriteProperty { + 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?) { + setPropertyNode(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 = object : ReadWriteProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): Value? = + getProperty(name ?: Name.parse(property.name), inherit, includeStyles, includeDefaults)?.value + + override fun setValue(thisRef: Any?, property: KProperty<*>, value: Value?) { + setPropertyValue(name ?: Name.parse(property.name), value) + } +} + +public fun 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 = object : ReadWriteProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): T = getProperty( + name ?: Name.parse(property.name), + inherit, + includeStyles, + includeDefaults + )?.value.let(getter) + + override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { + setPropertyValue(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 = 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 = propertyValue(name, inherit, includeStyles, includeDefaults) { + it?.number ?: default() +} \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/visionDescriptor.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/visionDescriptor.kt index c56b495e..de505c50 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/visionDescriptor.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/visionDescriptor.kt @@ -2,87 +2,63 @@ package space.kscience.visionforge import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.descriptors.* -import space.kscience.dataforge.names.Name -import space.kscience.dataforge.values.ValueType +import space.kscience.dataforge.names.asName import space.kscience.dataforge.values.asValue private const val INHERITED_DESCRIPTOR_ATTRIBUTE = "inherited" private const val STYLE_DESCRIPTOR_ATTRIBUTE = "useStyles" -public val ItemDescriptor.inherited: Boolean +public val MetaDescriptor.inherited: Boolean get() = attributes[INHERITED_DESCRIPTOR_ATTRIBUTE].boolean ?: false -public var ItemDescriptorBuilder.inherited: Boolean +public var MetaDescriptorBuilder.inherited: Boolean get() = attributes[INHERITED_DESCRIPTOR_ATTRIBUTE].boolean ?: false - set(value) = attributes { - set(INHERITED_DESCRIPTOR_ATTRIBUTE, value) - } + set(value) = attributes.set(INHERITED_DESCRIPTOR_ATTRIBUTE, value) -public val ItemDescriptor.usesStyles: Boolean + +public val MetaDescriptor.usesStyles: Boolean get() = attributes[STYLE_DESCRIPTOR_ATTRIBUTE].boolean ?: true -public var ItemDescriptorBuilder.usesStyles: Boolean +public var MetaDescriptorBuilder.usesStyles: Boolean get() = attributes[STYLE_DESCRIPTOR_ATTRIBUTE].boolean ?: true - set(value) = attributes { - set(STYLE_DESCRIPTOR_ATTRIBUTE, value) - } + set(value) = attributes.set(STYLE_DESCRIPTOR_ATTRIBUTE, value) public val Vision.describedProperties: Meta get() = Meta { - descriptor?.items?.forEach { (key, descriptor) -> - key put getProperty(key, inherit = descriptor.inherited) + descriptor?.children?.forEach { (key, descriptor) -> + this.setMeta(key.asName(), getProperty(key, inherit = descriptor.inherited)) } } -public val ValueDescriptor.widget: Meta - get() = attributes["widget"].node ?: Meta.EMPTY +public val MetaDescriptor.widget: Meta + get() = attributes["widget"] ?: Meta.EMPTY /** * Extension property to access the "widget" key of [ValueDescriptor] */ -public var ValueDescriptorBuilder.widget: Meta - get() = attributes["widget"].node ?: Meta.EMPTY +public var MetaDescriptorBuilder.widget: Meta + get() = attributes["widget"] ?: Meta.EMPTY set(value) { - attributes { - set("widget", value) - } + attributes["widget"] = value } -public val ValueDescriptor.widgetType: String? +public val MetaDescriptor.widgetType: String? get() = attributes["widget.type"].string /** * Extension property to access the "widget.type" key of [ValueDescriptor] */ -public var ValueDescriptorBuilder.widgetType: String? +public var MetaDescriptorBuilder.widgetType: String? get() = attributes["widget.type"].string set(value) { - attributes { - set("widget.type", value) - } + attributes["widget.type"] = value?.asValue() } /** * If true, this item is hidden in property editor. Default is false */ -public val ItemDescriptor.hidden: Boolean +public val MetaDescriptor.hidden: Boolean get() = attributes["widget.hide"].boolean ?: false -public fun ItemDescriptorBuilder.hide(): Unit = attributes { - set("widget.hide", true) -} - - -public inline fun > NodeDescriptorBuilder.enum( - key: Name, - default: E?, - crossinline modifier: ValueDescriptorBuilder.() -> Unit = {}, -): Unit = value(key) { - type(ValueType.STRING) - default?.let { - default(default) - } - allowedValues = enumValues().map { it.asValue() } - modifier() -} \ No newline at end of file +public fun MetaDescriptorBuilder.hide(): Unit = attributes.set("widget.hide", true) \ No newline at end of file diff --git a/visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/html/HtmlTagTest.kt b/visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/html/HtmlTagTest.kt index 1199b89b..2a7c6d28 100644 --- a/visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/html/HtmlTagTest.kt +++ b/visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/html/HtmlTagTest.kt @@ -57,7 +57,7 @@ class HtmlTagTest { div { h2 { +"Properties" } ul { - (vision as? VisionBase)?.meta?.items?.forEach { + (vision as? VisionBase)?.meta()?.items?.forEach { li { a { +it.key.toString() } p { +it.value.toString() } diff --git a/visionforge-core/src/jsMain/kotlin/space/kscience/visionforge/VisionClient.kt b/visionforge-core/src/jsMain/kotlin/space/kscience/visionforge/VisionClient.kt index 017c615d..ae691d4b 100644 --- a/visionforge-core/src/jsMain/kotlin/space/kscience/visionforge/VisionClient.kt +++ b/visionforge-core/src/jsMain/kotlin/space/kscience/visionforge/VisionClient.kt @@ -92,7 +92,7 @@ public class VisionClient : AbstractPlugin() { } logger.debug { "Got update $change for output with name $name" } - vision.update(change) + vision.change(change) } else { console.error("WebSocket message data is not a string") } diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/FXPlugin.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/FXPlugin.kt index 5e7994ef..d8210a13 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/FXPlugin.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/FXPlugin.kt @@ -115,5 +115,5 @@ public class ApplicationSurrogate : App() { } public fun Context.display(width: Double = 800.0, height: Double = 600.0, component: () -> UIComponent) { - plugins.fetch(FXPlugin).display(component(), width, height) + fetch(FXPlugin).display(component(), width, height) } \ No newline at end of file diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/ComboBoxValueChooser.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/ComboBoxValueChooser.kt index a3d5c942..a99599ee 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/ComboBoxValueChooser.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/ComboBoxValueChooser.kt @@ -9,8 +9,8 @@ import javafx.collections.FXCollections import javafx.scene.control.ComboBox import javafx.util.StringConverter import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.descriptors.allowedValues import space.kscience.dataforge.meta.get -import space.kscience.dataforge.meta.value import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.asName import space.kscience.dataforge.values.Value @@ -56,7 +56,7 @@ public class ComboBoxValueChooser(public val values: Collection? = null) override val name: Name = "combo".asName() override fun invoke(meta: Meta): ValueChooser = - ComboBoxValueChooser(meta["values"].value?.list) + ComboBoxValueChooser(meta["values"]?.value?.list) } } diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/ConfigEditor.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/ConfigEditor.kt deleted file mode 100644 index 76d32f57..00000000 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/ConfigEditor.kt +++ /dev/null @@ -1,189 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package space.kscience.visionforge.editor - -import de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon -import de.jensd.fx.glyphs.fontawesome.FontAwesomeIconView -import javafx.scene.Node -import javafx.scene.control.* -import javafx.scene.control.cell.TextFieldTreeTableCell -import javafx.scene.layout.BorderPane -import javafx.scene.layout.HBox -import javafx.scene.layout.Priority -import javafx.scene.paint.Color -import javafx.scene.text.Text -import space.kscience.dataforge.context.Global -import space.kscience.dataforge.meta.Config -import space.kscience.dataforge.meta.descriptors.NodeDescriptor -import space.kscience.dataforge.names.NameToken -import space.kscience.visionforge.dfIconView -import tornadofx.* - -/** - * A configuration editor fragment - * - * @author Alexander Nozik - */ -public class ConfigEditor( - public val rootNode: FXMetaNode, - public val allowNew: Boolean = true, - title: String = "Configuration editor" -) : Fragment(title = title, icon = dfIconView) { - //TODO replace parameters by properties - - public constructor(config: Config, descriptor: NodeDescriptor?, title: String = "Configuration editor") : - this(FXMeta.root(config, descriptor = descriptor), title = title) - - override val root: BorderPane = borderpane { - center = treetableview> { - root = TreeItem(rootNode) - root.isExpanded = true - sortMode = TreeSortMode.ALL_DESCENDANTS - columnResizePolicy = TreeTableView.CONSTRAINED_RESIZE_POLICY - populate { - when (val fxMeta = it.value) { - is FXMetaNode -> { - fxMeta.children - } - is FXMetaValue -> null - } - } - column("Name", FXMeta::name) { - setCellFactory { - object : TextFieldTreeTableCell, NameToken>() { - override fun updateItem(item: NameToken?, empty: Boolean) { - super.updateItem(item, empty) - contextMenu?.items?.removeIf { it.text == "Remove" } - if (!empty) { - if (treeTableRow.item != null) { - textFillProperty().bind(treeTableRow.item.hasValue.objectBinding { - if (it == true) { - Color.BLACK - } else { - Color.GRAY - } - }) - if (treeTableRow.treeItem.value.parent != null && treeTableRow.treeItem.value.hasValue.get()) { - contextmenu { - item("Remove") { - action { - treeTableRow.item.remove() - } - } - } - } - } - } - } - } - } - } - - column("Value") { param: TreeTableColumn.CellDataFeatures, FXMeta> -> - param.value.valueProperty() - }.setCellFactory { - ValueCell() - } - - column("Description") { param: TreeTableColumn.CellDataFeatures, String> -> param.value.value.descriptionProperty } - .setCellFactory { param: TreeTableColumn, String> -> - val cell = TreeTableCell, String>() - val text = Text() - cell.graphic = text - cell.prefHeight = Control.USE_COMPUTED_SIZE - text.wrappingWidthProperty().bind(param.widthProperty()) - text.textProperty().bind(cell.itemProperty()) - cell - } - } - } - - private fun showNodeDialog(): String? { - val dialog = TextInputDialog() - dialog.title = "Node name selection" - dialog.contentText = "Enter a name for new node: " - dialog.headerText = null - - val result = dialog.showAndWait() - return result.orElse(null) - } - - private fun showValueDialog(): String? { - val dialog = TextInputDialog() - dialog.title = "Value name selection" - dialog.contentText = "Enter a name for new value: " - dialog.headerText = null - - val result = dialog.showAndWait() - return result.orElse(null) - } - - private inner class ValueCell : TreeTableCell, FXMeta?>() { - - public override fun updateItem(item: FXMeta?, empty: Boolean) { - if (!empty) { - if (item != null) { - when (item) { - is FXMetaValue -> { - text = null - val chooser = ValueChooser.build( - Global, - item.valueProperty, - item.descriptor - ) { - item.set(it) - } - graphic = chooser.node - } - is FXMetaNode -> { - if (allowNew) { - text = null - graphic = HBox().apply { - val glyph: Node = FontAwesomeIconView(FontAwesomeIcon.PLUS_CIRCLE) - button("node", graphic = glyph) { - hgrow = Priority.ALWAYS - maxWidth = Double.POSITIVE_INFINITY - action { - showNodeDialog()?.let { - item.addNode(it) - } - } - } - button("value", graphic = FontAwesomeIconView(FontAwesomeIcon.PLUS_SQUARE)) { - hgrow = Priority.ALWAYS - maxWidth = Double.POSITIVE_INFINITY - action { - showValueDialog()?.let { - item.addValue(it) - } - } - } - } - } else { - text = "" - } - } - } - - } else { - text = null - graphic = null - } - } else { - text = null - graphic = null - } - } - - } - - companion object { - /** - * The tag not to display node or value in configurator - */ - const val NO_CONFIGURATOR_TAG = "nocfg" - } -} diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/FXMeta.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/FXMeta.kt deleted file mode 100644 index 3e2cfc8d..00000000 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/FXMeta.kt +++ /dev/null @@ -1,223 +0,0 @@ -package space.kscience.visionforge.editor - -import javafx.beans.binding.ListBinding -import javafx.beans.binding.ObjectBinding -import javafx.beans.property.SimpleObjectProperty -import javafx.beans.value.ObservableBooleanValue -import javafx.beans.value.ObservableStringValue -import javafx.collections.ObservableList -import space.kscience.dataforge.meta.* -import space.kscience.dataforge.meta.descriptors.ItemDescriptor -import space.kscience.dataforge.meta.descriptors.NodeDescriptor -import space.kscience.dataforge.meta.descriptors.ValueDescriptor -import space.kscience.dataforge.names.* -import space.kscience.dataforge.values.Null -import space.kscience.dataforge.values.Value -import tornadofx.* - -/** - * A display for meta and descriptor - */ -sealed class FXMeta> : Comparable> { - abstract val name: NameToken - abstract val parent: FXMetaNode? - abstract val descriptionProperty: ObservableStringValue - abstract val descriptor: ItemDescriptor? - - abstract val hasValue: ObservableBooleanValue - - override fun compareTo(other: FXMeta<*>): Int { - return if (this.hasValue.get() == other.hasValue.get()) { - this.name.toString().compareTo(other.name.toString()) - } else { - this.hasValue.get().compareTo(other.hasValue.get()) - } - } - - companion object { - fun > root( - node: M, - descriptor: NodeDescriptor? = null, - rootName: String = "root" - ): FXMetaNode = - FXMetaNode(NameToken(rootName), null, node, descriptor) - - fun root(node: Meta, descriptor: NodeDescriptor? = null, rootName: String = "root"): FXMetaNode = - root(node.seal(), descriptor, rootName) - } -} - -class FXMetaNode>( - override val name: NameToken, - override val parent: FXMetaNode?, - nodeValue: M? = null, - descriptorValue: NodeDescriptor? = null -) : FXMeta() { - - /** - * A descriptor that could be manually set to the node - */ - private val innerDescriptorProperty = SimpleObjectProperty(descriptorValue) - - /** - * Actual descriptor which holds value inferred from parrent - */ - val descriptorProperty = objectBinding(innerDescriptorProperty) { - value ?: parent?.descriptor?.nodes?.get(this@FXMetaNode.name.body) - } - - override val descriptor: NodeDescriptor? by descriptorProperty - - private val innerNodeProperty = SimpleObjectProperty(nodeValue) - - val nodeProperty: ObjectBinding = objectBinding(innerNodeProperty) { - value ?: parent?.node?.get(this@FXMetaNode.name).node - } - - val node: M? by nodeProperty - - override val descriptionProperty = innerDescriptorProperty.stringBinding { it?.info ?: "" } - - override val hasValue: ObservableBooleanValue = nodeProperty.booleanBinding { it != null } - - private val filter: (FXMeta) -> Boolean = { cfg -> - !(cfg.descriptor?.attributes?.get(ConfigEditor.NO_CONFIGURATOR_TAG)?.boolean ?: false) - } - - val children = object : ListBinding>() { - - init { - bind(nodeProperty, descriptorProperty) - - val listener: (Name, MetaItem?, MetaItem?) -> Unit = { name, _, _ -> - if (name.length == 1) invalidate() - } - - (node as? Config)?.onChange(this, listener) - - nodeProperty.addListener { _, oldValue, newValue -> - if (newValue == null) { - (oldValue as? Config)?.removeListener(this) - } - - if (newValue is Config) { - newValue.onChange(this, listener) - } - } - } - - override fun computeValue(): ObservableList> { - val nodeKeys = node?.items?.keys?.toSet() ?: emptySet() - val descriptorKeys = descriptor?.items?.keys?.map { NameToken(it) } ?: emptyList() - val keys: Set = nodeKeys + descriptorKeys - - val items = keys.map { token -> - val actualItem = node?.items?.get(token) - val actualDescriptor = descriptor?.items?.get(token.body) - - if (actualItem is MetaItemNode || actualDescriptor is NodeDescriptor) { - FXMetaNode(token, this@FXMetaNode) - } else { - FXMetaValue(token, this@FXMetaNode) - } - } - - return items.filter(filter).asObservable() - } - } - - init { - if (parent != null) { - parent.descriptorProperty.onChange { descriptorProperty.invalidate() } - parent.nodeProperty.onChange { nodeProperty.invalidate() } - } - } -} - -public class FXMetaValue>( - override val name: NameToken, - override val parent: FXMetaNode -) : FXMeta() { - - public val descriptorProperty = parent.descriptorProperty.objectBinding { - it?.values?.get(name.body) - } - - /** - * A descriptor that could be manually set to the node - */ - override val descriptor: ValueDescriptor? by descriptorProperty - - //private val innerValueProperty = SimpleObjectProperty(value) - - public val valueProperty = descriptorProperty.objectBinding { descriptor -> - parent.node?.get(name).value ?: descriptor?.default - } - - override val hasValue: ObservableBooleanValue = parent.nodeProperty.booleanBinding { it?.get(name) != null } - - public val value by valueProperty - - override val descriptionProperty = descriptorProperty.stringBinding { it?.info ?: "" } -} - -public fun > FXMetaNode.remove(name: NameToken) { - node?.remove(name.asName()) - children.invalidate() -} - -private fun > M.createEmptyNode(token: NameToken, append: Boolean): M { - return if (append && token.hasIndex()) { - val name = token.asName() - val index = (getIndexed(name).keys.mapNotNull { it?.toIntOrNull() }.maxOrNull() ?: -1) + 1 - val newName = name.withIndex(index.toString()) - set(newName, Meta.EMPTY) - get(newName).node!! - } else { - this.set(token.asName(), Meta.EMPTY) - //FIXME possible concurrency bug - get(token).node!! - } -} - -fun > FXMetaNode.getOrCreateNode(): M { - val node = node - return when { - node != null -> node - parent != null -> parent.getOrCreateNode().createEmptyNode(this.name, descriptor?.multiple == true).also { - parent.children.invalidate() - } - else -> kotlin.error("Orphan empty node is not allowed") - } - -} - -fun > FXMeta.remove() { - parent?.node?.remove(name.asName()) -} - -fun > FXMetaNode.addValue(key: String) { - val parent = getOrCreateNode() - if (descriptor?.multiple == true) { - parent.append(key, Null) - } else { - parent[key] = Null - } -} - -fun > FXMetaNode.addNode(key: String) { - val parent = getOrCreateNode() - if (descriptor?.multiple == true) { - parent.append(key, Meta.EMPTY) - } else { - parent[key] = Meta.EMPTY - } -} - -fun > FXMetaValue.set(value: Value?) { - if (descriptor?.multiple == true) { - parent.getOrCreateNode().append(this.name.body, value) - } else { - parent.getOrCreateNode()[this.name] = value - } -} \ No newline at end of file diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/FXMetaModel.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/FXMetaModel.kt new file mode 100644 index 00000000..a3509221 --- /dev/null +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/FXMetaModel.kt @@ -0,0 +1,217 @@ +package space.kscience.visionforge.editor + +import javafx.beans.binding.Binding +import javafx.beans.binding.BooleanBinding +import javafx.beans.binding.ListBinding +import javafx.beans.property.SimpleObjectProperty +import javafx.collections.ObservableList +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 space.kscience.dataforge.values.Value +import tornadofx.* + +/** + * A display for meta and descriptor + */ +public class FXMetaModel( + public val root: M, + public val rootDescriptor: MetaDescriptor?, + public val nodeName: Name, + public val title: String = nodeName.lastOrNull()?.toString() ?: "Meta" +) : Comparable> { + + private val existingNode = SimpleObjectProperty(root[nodeName]) + + public val children: ListBinding> = object : ListBinding>() { + override fun computeValue(): ObservableList> { + val nodeKeys = existingNode.get().items.keys + val descriptorKeys = descriptor?.children?.keys?.map { NameToken(it) } ?: emptySet() + return (nodeKeys + descriptorKeys).map { + FXMetaModel( + root, + rootDescriptor, + nodeName + it + ) + }.filter(filter).asObservable() + } + } + + init { + //add listener to the root node if possible + if (root is ObservableMeta) { + root.onChange(this) { changed -> + if (changed.startsWith(nodeName)) { + if (nodeName.length == changed.length) existingNode.set(root[nodeName]) + else if (changed.length == nodeName.length + 1) children.invalidate() + } + } + } + } + + public val descriptor: MetaDescriptor? = rootDescriptor?.get(nodeName) + + public val existsProperty: BooleanBinding = existingNode.isNotNull + + public val exists: Boolean by existsProperty + + public val valueProperty: Binding = existingNode.objectBinding { + existingNode.get().value ?: descriptor?.defaultValue + } + + override fun compareTo(other: FXMetaModel<*>): Int = if (this.exists == other.exists) { + this.nodeName.toString().compareTo(other.nodeName.toString()) + } else { + this.exists.compareTo(other.exists) + } + + public companion object { + private val filter: (FXMetaModel<*>) -> Boolean = { cfg -> + !(cfg.descriptor?.attributes?.get(MutableMetaEditor.NO_CONFIGURATOR_TAG)?.boolean ?: false) + } + + public fun root( + node: M, + descriptor: MetaDescriptor? = null, + rootName: String = "root" + ): FXMetaModel = FXMetaModel(node, descriptor, Name.EMPTY, title = rootName) + } + +// /** +// * A descriptor that could be manually set to the node +// */ +// private val innerDescriptorProperty = SimpleObjectProperty(descriptorValue) +// +// /** +// * Actual descriptor which holds value inferred from parrent +// */ +// val descriptorProperty: ObjectBinding = objectBinding(innerDescriptorProperty) { +// value ?: parent?.descriptor?.get(this@FXMeta.name.body) +// } +// +// val descriptor: MetaDescriptor? by descriptorProperty +// +// private val innerNodeProperty = SimpleObjectProperty(nodeValue) +// +// val nodeProperty: ObjectBinding = objectBinding(innerNodeProperty) { +// value ?: parent?.node?.get(this@FXMeta.name) +// } +// +// val node by nodeProperty +// +// val hasValue: ObservableBooleanValue = nodeProperty.booleanBinding { it != null } +// +// private val filter: (FXMeta) -> Boolean = { cfg -> +// !(cfg.descriptor?.attributes?.get(MutableMetaEditor.NO_CONFIGURATOR_TAG)?.boolean ?: false) +// } +// +// val children: ListBinding> = object : ListBinding>() { +// +// init { +// bind(nodeProperty, descriptorProperty) +// +// val listener: Meta.(Name) -> Unit = { name -> +// if (name.length == 1) invalidate() +// } +// +// (node as? ObservableMeta)?.onChange(this, listener) +// +// nodeProperty.addListener { _, oldValue, newValue -> +// if (newValue == null) { +// (oldValue as? ObservableMeta)?.removeListener(this) +// } +// +// if (newValue is ObservableMeta) { +// newValue.onChange(this, listener) +// } +// } +// } +// +// override fun computeValue(): ObservableList> { +// val nodeKeys = node?.items?.keys?.toSet() ?: emptySet() +// val descriptorKeys = descriptor?.children?.keys?.map { NameToken(it) } ?: emptyList() +// val keys: Set = nodeKeys + descriptorKeys +// +// val items = keys.map { token -> +// val actualItem = node?.items?.get(token) +// val actualDescriptor = descriptor?.children?.get(token.body) +// +// if (actualItem is MetaNode) { +// FXMetaNode(token, this@FXMetaNode) +// } else { +// FXMetaValue(token, this@FXMetaNode) +// } +// } +// +// return items.filter(filter).asObservable() +// } +// } +// +// init { +// if (parent != null) { +// parent.descriptorProperty.onChange { descriptorProperty.invalidate() } +// parent.nodeProperty.onChange { nodeProperty.invalidate() } +// } +// } +// +} + +// +//internal fun FXMeta.remove(name: NameToken) { +// node?.remove(name.asName()) +// children.invalidate() +//} +// +//private fun M.createEmptyNode(token: NameToken, append: Boolean): M { +// return if (append && token.hasIndex()) { +// val name = token.asName() +// val index = (getIndexed(name).keys.mapNotNull { it?.toIntOrNull() }.maxOrNull() ?: -1) + 1 +// val newName = name.withIndex(index.toString()) +// set(newName, Meta.EMPTY) +// get(newName).node +// } else { +// this.set(token.asName(), Meta.EMPTY) +// //FIXME possible concurrency bug +// get(token).node +// } +//} +// +//internal fun FXMeta.getOrCreateNode(): M { +// val node = node +// return when { +// node != null -> node +// parent != null -> parent.getOrCreateNode().createEmptyNode(this.name, descriptor?.multiple == true).also { +// parent.children.invalidate() +// } +// else -> kotlin.error("Orphan empty node is not allowed") +// } +// +//} + +internal fun FXMetaModel.remove() { + root.remove(nodeName) +} + +// +//internal fun FXMeta.addValue(key: String) { +// val parent = getOrCreateNode() +// if (descriptor?.multiple == true) { +// parent.append(key, Null) +// } else { +// parent[key] = Null +// } +//} +// +//internal fun FXMeta.addNode(key: String) { +// val parent = getOrCreateNode() +// if (descriptor?.multiple == true) { +// parent.append(key, Meta.EMPTY) +// } else { +// parent[key] = Meta.EMPTY +// } +//} +// +internal fun FXMetaModel.setValue(value: Value?) { + root.setValue(nodeName, value) +} \ No newline at end of file diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/MetaViewer.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/MetaViewer.kt index 1a822793..92c1222f 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/MetaViewer.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/MetaViewer.kt @@ -16,45 +16,42 @@ package space.kscience.visionforge.editor -import javafx.beans.property.SimpleStringProperty import javafx.scene.control.TreeItem import javafx.scene.control.TreeSortMode import javafx.scene.control.TreeTableView +import javafx.scene.layout.BorderPane import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.values.string import space.kscience.visionforge.dfIconView import tornadofx.* -class MetaViewer(val rootNode: FXMetaNode<*>, title: String = "Meta viewer") : Fragment(title, - dfIconView -) { - constructor(meta: Meta, title: String = "Meta viewer"): this( - FXMeta.root( - meta - ),title = title) +public class MetaViewer( + private val rootNode: FXMetaModel, + title: String = "Meta viewer" +) : Fragment(title, dfIconView) { - override val root = borderpane { + public constructor(meta: Meta, title: String = "Meta viewer") : this( + FXMetaModel.root( + meta + ), title = title + ) + + override val root: BorderPane = borderpane { center { - treetableview> { + treetableview> { isShowRoot = false root = TreeItem(rootNode) populate { - when (val fxMeta = it.value) { - is FXMetaNode -> { - fxMeta.children - } - is FXMetaValue -> null - } + val fxMeta = it.value + fxMeta.children } root.isExpanded = true sortMode = TreeSortMode.ALL_DESCENDANTS columnResizePolicy = TreeTableView.CONSTRAINED_RESIZE_POLICY - column("Name", FXMeta<*>::name) - column, String>("Value") { cellDataFeatures -> - when (val item = cellDataFeatures.value.value) { - is FXMetaValue -> item.valueProperty.stringBinding { it?.string ?: "" } - is FXMetaNode -> SimpleStringProperty("[node]") - } + column("Name", FXMetaModel<*>::title) + column, String>("Value") { cellDataFeatures -> + val item = cellDataFeatures.value.value + item.valueProperty.stringBinding { it?.string ?: "" } } } } diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/MutableMetaEditor.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/MutableMetaEditor.kt new file mode 100644 index 00000000..c7238b44 --- /dev/null +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/MutableMetaEditor.kt @@ -0,0 +1,193 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package space.kscience.visionforge.editor + +import javafx.scene.control.* +import javafx.scene.control.cell.TextFieldTreeTableCell +import javafx.scene.layout.BorderPane +import javafx.scene.paint.Color +import javafx.scene.text.Text +import space.kscience.dataforge.context.Global +import space.kscience.dataforge.meta.MutableMeta +import space.kscience.dataforge.meta.descriptors.MetaDescriptor +import space.kscience.visionforge.dfIconView +import tornadofx.* + +/** + * A Configuration editor fragment + * + * @author Alexander Nozik + */ +public class MutableMetaEditor( + public val rootNode: FXMetaModel, + public val allowNew: Boolean = true, + title: String = "Configuration editor" +) : Fragment(title = title, icon = dfIconView) { + //TODO replace parameters by properties + + public constructor( + MutableMeta: MutableMeta, + descriptor: MetaDescriptor?, + title: String = "Configuration editor" + ) : + this(FXMetaModel.root(MutableMeta, descriptor = descriptor), title = title) + + override val root: BorderPane = borderpane { + center = treetableview> { + root = TreeItem(rootNode) + root.isExpanded = true + sortMode = TreeSortMode.ALL_DESCENDANTS + columnResizePolicy = TreeTableView.CONSTRAINED_RESIZE_POLICY + populate { + it.value.children + } + column("Name", FXMetaModel::title) { + setCellFactory { + object : TextFieldTreeTableCell, String>() { + override fun updateItem(item: String?, empty: Boolean) { + super.updateItem(item, empty) + contextMenu?.items?.removeIf { it.text == "Remove" } + val content = treeTableRow.item + if (!empty) { + if (treeTableRow.item != null) { + textFillProperty().bind(content.existsProperty.objectBinding { + if (it == true) { + Color.BLACK + } else { + Color.GRAY + } + }) + if (content.exists) { + contextmenu { + item("Remove") { + action { + content.remove() + } + } + } + } + } + } + } + } + } + } + + column("Value") { param: TreeTableColumn.CellDataFeatures, FXMetaModel> -> + param.value.valueProperty() + }.setCellFactory { + ValueCell() + } + + column("Description") { param: TreeTableColumn.CellDataFeatures, String> -> + (param.value.value.descriptor?.info ?: "").observable() + }.setCellFactory { param: TreeTableColumn, String> -> + val cell = TreeTableCell, String>() + val text = Text() + cell.graphic = text + cell.prefHeight = Control.USE_COMPUTED_SIZE + text.wrappingWidthProperty().bind(param.widthProperty()) + text.textProperty().bind(cell.itemProperty()) + cell + } + } + } + + private fun showNodeDialog(): String? { + val dialog = TextInputDialog() + dialog.title = "Node name selection" + dialog.contentText = "Enter a name for new node: " + dialog.headerText = null + + val result = dialog.showAndWait() + return result.orElse(null) + } + + private fun showValueDialog(): String? { + val dialog = TextInputDialog() + dialog.title = "Value name selection" + dialog.contentText = "Enter a name for new value: " + dialog.headerText = null + + val result = dialog.showAndWait() + return result.orElse(null) + } + + private inner class ValueCell : TreeTableCell, FXMetaModel?>() { + + public override fun updateItem(item: FXMetaModel?, empty: Boolean) { + if (!empty) { + if (item != null) { + text = null + val chooser = ValueChooser.build( + Global, + item.valueProperty, + item.descriptor + ) { + item.setValue(it) + } + graphic = chooser.node +// when (item) { +// is FXMetaValue -> { +// text = null +// val chooser = ValueChooser.build( +// Global, +// item.valueProperty, +// item.descriptor +// ) { +// item.set(it) +// } +// graphic = chooser.node +// } +// is FXMetaNode -> { +// if (allowNew) { +// text = null +// graphic = HBox().apply { +// val glyph: Node = FontAwesomeIconView(FontAwesomeIcon.PLUS_CIRCLE) +// button("node", graphic = glyph) { +// hgrow = Priority.ALWAYS +// maxWidth = Double.POSITIVE_INFINITY +// action { +// showNodeDialog()?.let { +// item.addNode(it) +// } +// } +// } +// button("value", graphic = FontAwesomeIconView(FontAwesomeIcon.PLUS_SQUARE)) { +// hgrow = Priority.ALWAYS +// maxWidth = Double.POSITIVE_INFINITY +// action { +// showValueDialog()?.let { +// item.addValue(it) +// } +// } +// } +// } +// } else { +// text = "" +// } +// } +// } + + } else { + text = null + graphic = null + } + } else { + text = null + graphic = null + } + } + + } + + public companion object { + /** + * The tag not to display node or value in MutableMetaurator + */ + public const val NO_CONFIGURATOR_TAG: String = "nocfg" + } +} diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/TextValueChooser.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/TextValueChooser.kt index ea55fc83..9a1840ce 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/TextValueChooser.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/TextValueChooser.kt @@ -10,12 +10,13 @@ import javafx.scene.control.TextField import javafx.scene.input.KeyCode import javafx.scene.input.KeyEvent import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.descriptors.validate import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.asName import space.kscience.dataforge.values.* import tornadofx.* -class TextValueChooser : ValueChooserBase() { +public class TextValueChooser : ValueChooserBase() { private val displayText: String get() = currentValue().let { @@ -85,7 +86,7 @@ class TextValueChooser : ValueChooserBase() { } private fun validate(value: Value): Boolean { - return descriptor?.isAllowedValue(value) ?: true + return descriptor?.validate(value) ?: true } // @Override @@ -101,7 +102,7 @@ class TextValueChooser : ValueChooserBase() { } } - companion object : ValueChooser.Factory { + public companion object : ValueChooser.Factory { override val name: Name = "text".asName() override fun invoke(meta: Meta): ValueChooser = TextValueChooser() diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/ValueCallback.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/ValueCallback.kt index 32a0d741..a4e72871 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/ValueCallback.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/ValueCallback.kt @@ -13,11 +13,11 @@ import space.kscience.dataforge.values.Value * @param value Value after change * @param message Message on unsuccessful change */ -class ValueCallbackResponse(val success: Boolean, val value: Value, val message: String) +public class ValueCallbackResponse(public val success: Boolean, public val value: Value, public val message: String) /** * A callback for some visual object trying to change some value * @author [Alexander Nozik](mailto:altavir@gmail.com) */ -typealias ValueCallback = (Value) -> ValueCallbackResponse +public typealias ValueCallback = (Value) -> ValueCallbackResponse diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/ValueChooser.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/ValueChooser.kt index c3796fa0..99616b61 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/ValueChooser.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/ValueChooser.kt @@ -10,10 +10,12 @@ import javafx.beans.value.ObservableValue import javafx.scene.Node import space.kscience.dataforge.context.Context import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.meta.descriptors.ValueDescriptor +import space.kscience.dataforge.meta.descriptors.MetaDescriptor +import space.kscience.dataforge.meta.descriptors.allowedValues +import space.kscience.dataforge.meta.descriptors.validate import space.kscience.dataforge.misc.Named import space.kscience.dataforge.misc.Type -import space.kscience.dataforge.names.toName +import space.kscience.dataforge.names.Name import space.kscience.dataforge.provider.provideByType import space.kscience.dataforge.values.Null import space.kscience.dataforge.values.Value @@ -42,8 +44,8 @@ public interface ValueChooser { * * @return */ - public val descriptorProperty: ObjectProperty - public var descriptor: ValueDescriptor? + public val descriptorProperty: ObjectProperty + public var descriptor: MetaDescriptor? public val valueProperty: ObjectProperty public var value: Value? @@ -71,7 +73,7 @@ public interface ValueChooser { public companion object { private fun findWidgetByType(context: Context, type: String): Factory? { - return when (type.toName()) { + return when (Name.parse(type)) { TextValueChooser.name -> TextValueChooser ColorValueChooser.name -> ColorValueChooser ComboBoxValueChooser.name -> ComboBoxValueChooser @@ -79,7 +81,7 @@ public interface ValueChooser { } } - private fun build(context: Context, descriptor: ValueDescriptor?): ValueChooser { + private fun build(context: Context, descriptor: MetaDescriptor?): ValueChooser { return if (descriptor == null) { TextValueChooser(); } else { @@ -93,7 +95,7 @@ public interface ValueChooser { descriptor.widget ) ?: TextValueChooser() } - descriptor.allowedValues.isNotEmpty() -> ComboBoxValueChooser() + !descriptor.allowedValues.isNullOrEmpty() -> ComboBoxValueChooser() else -> TextValueChooser() } chooser.descriptor = descriptor @@ -101,10 +103,10 @@ public interface ValueChooser { } } - fun build( + public fun build( context: Context, value: ObservableValue, - descriptor: ValueDescriptor? = null, + descriptor: MetaDescriptor? = null, setter: (Value) -> Unit, ): ValueChooser { val chooser = build(context, descriptor) @@ -113,7 +115,7 @@ public interface ValueChooser { chooser.setDisplayValue(it ?: Null) } chooser.setCallback { result -> - if (descriptor?.isAllowedValue(result) != false) { + if (descriptor?.validate(result) != false) { setter(result) ValueCallbackResponse(true, result, "OK") } else { diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/ValueChooserBase.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/ValueChooserBase.kt index 81dc42ac..e9b61886 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/ValueChooserBase.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/ValueChooserBase.kt @@ -8,7 +8,7 @@ package space.kscience.visionforge.editor import javafx.beans.property.SimpleObjectProperty import javafx.scene.Node import org.slf4j.LoggerFactory -import space.kscience.dataforge.meta.descriptors.ValueDescriptor +import space.kscience.dataforge.meta.descriptors.MetaDescriptor import space.kscience.dataforge.values.Null import space.kscience.dataforge.values.Value import tornadofx.* @@ -18,16 +18,16 @@ import tornadofx.* * * @author Alexander Nozik */ -abstract class ValueChooserBase : ValueChooser { +public abstract class ValueChooserBase : ValueChooser { - override val node by lazy { buildNode() } - final override val valueProperty = SimpleObjectProperty(Null) - final override val descriptorProperty = SimpleObjectProperty() + override val node: T by lazy { buildNode() } + final override val valueProperty: SimpleObjectProperty = SimpleObjectProperty(Null) + final override val descriptorProperty: SimpleObjectProperty = SimpleObjectProperty() - override var descriptor: ValueDescriptor? by descriptorProperty + override var descriptor: MetaDescriptor? by descriptorProperty override var value: Value? by valueProperty - fun resetValue() { + public fun resetValue() { setDisplayValue(currentValue()) } @@ -36,7 +36,7 @@ abstract class ValueChooserBase : ValueChooser { * @return */ protected fun currentValue(): Value { - return value ?: descriptor?.default ?: Null + return value ?: descriptor?.defaultValue ?: Null } /** diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/VisualObjectEditorFragment.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/VisionEditorFragment.kt similarity index 57% rename from visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/VisualObjectEditorFragment.kt rename to visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/VisionEditorFragment.kt index 929c7e85..3ce3b2de 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/VisualObjectEditorFragment.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/VisionEditorFragment.kt @@ -5,39 +5,36 @@ import javafx.beans.property.SimpleObjectProperty import javafx.scene.Node import javafx.scene.Parent import javafx.scene.layout.VBox -import space.kscience.dataforge.meta.Config -import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.meta.MutableItemProvider -import space.kscience.dataforge.meta.descriptors.NodeDescriptor -import space.kscience.dataforge.meta.update +import space.kscience.dataforge.meta.* +import space.kscience.dataforge.meta.descriptors.MetaDescriptor import space.kscience.visionforge.* import tornadofx.* -class VisualObjectEditorFragment(val selector: (Vision) -> Meta) : Fragment() { +public class VisionEditorFragment(public val selector: (Vision) -> Meta) : Fragment() { - val itemProperty = SimpleObjectProperty() - var item: Vision? by itemProperty - val descriptorProperty = SimpleObjectProperty() + public val itemProperty: SimpleObjectProperty = SimpleObjectProperty() + public var item: Vision? by itemProperty + public val descriptorProperty: SimpleObjectProperty = SimpleObjectProperty() - constructor( + public constructor( item: Vision?, - descriptor: NodeDescriptor?, - selector: (Vision) -> MutableItemProvider = { it.allProperties() }, + descriptor: MetaDescriptor?, + selector: (Vision) -> MutableMetaProvider = { it.meta() }, ) : this({ it.describedProperties }) { this.item = item this.descriptorProperty.set(descriptor) } - private var currentConfig: Config? = null + private var currentConfig: ObservableMutableMeta? = null - private val configProperty: Binding = itemProperty.objectBinding { visualObject -> - if (visualObject == null) return@objectBinding null - val meta = selector(visualObject) - val config = Config().apply { + private val configProperty: Binding = itemProperty.objectBinding { vision -> + if (vision == null) return@objectBinding null + val meta = selector(vision) + val config = MutableMeta { update(meta) - onChange(this@VisualObjectEditorFragment) { key, _, after -> - visualObject.setProperty(key, after) - } + } + config.onChange(this@VisionEditorFragment) { key -> + vision.setPropertyNode(key, config[key]) } //remember old config reference to cleanup listeners currentConfig?.removeListener(this) @@ -47,7 +44,7 @@ class VisualObjectEditorFragment(val selector: (Vision) -> Meta) : Fragment() { private val configEditorProperty: Binding = configProperty.objectBinding(descriptorProperty) { it?.let { - ConfigEditor(it, descriptorProperty.get()).root + MutableMetaEditor(it, descriptorProperty.get()).root } } diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/VisualObjectTreeFragment.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/VisionTreeFragment.kt similarity index 82% rename from visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/VisualObjectTreeFragment.kt rename to visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/VisionTreeFragment.kt index f1faaf1a..65aa1b9c 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/VisualObjectTreeFragment.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/VisionTreeFragment.kt @@ -3,6 +3,7 @@ package space.kscience.visionforge.editor import javafx.beans.property.SimpleObjectProperty import javafx.scene.control.SelectionMode import javafx.scene.control.TreeItem +import javafx.scene.layout.VBox import space.kscience.visionforge.Vision import space.kscience.visionforge.VisionGroup import tornadofx.* @@ -29,13 +30,13 @@ private fun toTreeItem(vision: Vision, title: String): TreeItem() - var item: Vision? by itemProperty +public class VisionTreeFragment : Fragment() { + public val itemProperty: SimpleObjectProperty = SimpleObjectProperty() + public var item: Vision? by itemProperty - val selectedProperty = SimpleObjectProperty() + public val selectedProperty: SimpleObjectProperty = SimpleObjectProperty() - override val root = vbox { + override val root: VBox = vbox { titledpane("Object tree", collapsible = false) { treeview> { cellFormat { diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXMaterials.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXMaterials.kt index aa02d1ca..0b205626 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXMaterials.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXMaterials.kt @@ -3,11 +3,15 @@ package space.kscience.visionforge.solid import javafx.scene.paint.Color import javafx.scene.paint.Material import javafx.scene.paint.PhongMaterial -import space.kscience.dataforge.meta.* +import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.double +import space.kscience.dataforge.meta.get +import space.kscience.dataforge.meta.int import space.kscience.dataforge.values.ValueType import space.kscience.dataforge.values.int import space.kscience.dataforge.values.string import space.kscience.visionforge.Colors +import space.kscience.visionforge.solid.FXMaterials.GREY public object FXMaterials { public val RED: PhongMaterial = PhongMaterial().apply { @@ -26,46 +30,43 @@ public object FXMaterials { } public val BLUE: PhongMaterial = PhongMaterial(Color.BLUE) + } /** * Infer color based on meta item * @param opacity default opacity */ -public fun MetaItem.color(opacity: Double = 1.0): Color { - return when (this) { - is MetaItemValue -> if (this.value.type == ValueType.NUMBER) { - val int = value.int +public fun Meta.color(opacity: Double = 1.0): Color { + return value?.let { + if (it.type == ValueType.NUMBER) { + val int = it.int val red = int and 0x00ff0000 shr 16 val green = int and 0x0000ff00 shr 8 val blue = int and 0x000000ff Color.rgb(red, green, blue, opacity) } else { - Color.web(this.value.string) + Color.web(it.string) } - is MetaItemNode -> { - Color.rgb( - node[Colors.RED_KEY]?.int ?: 0, - node[Colors.GREEN_KEY]?.int ?: 0, - node[Colors.BLUE_KEY]?.int ?: 0, - node[SolidMaterial.OPACITY_KEY]?.double ?: opacity - ) - } - } + } ?: Color.rgb( + this[Colors.RED_KEY]?.int ?: 0, + this[Colors.GREEN_KEY]?.int ?: 0, + this[Colors.BLUE_KEY]?.int ?: 0, + this[SolidMaterial.OPACITY_KEY]?.double ?: opacity + ) } /** * Infer FX material based on meta item */ -public fun MetaItem?.material(): Material { - return when (this) { - null -> FXMaterials.GREY - is MetaItemValue -> PhongMaterial(color()) - is MetaItemNode -> PhongMaterial().apply { - val opacity = node[SolidMaterial.OPACITY_KEY].double ?: 1.0 - diffuseColor = node[SolidMaterial.COLOR_KEY]?.color(opacity) ?: Color.DARKGREY - specularColor = node[SolidMaterial.SPECULAR_COLOR_KEY]?.color(opacity) ?: Color.WHITE - } +public fun Meta?.material(): Material { + if (this == null) return GREY + return value?.let { + PhongMaterial(color()) + } ?: PhongMaterial().apply { + val opacity = get(SolidMaterial.OPACITY_KEY).double ?: 1.0 + diffuseColor = get(SolidMaterial.COLOR_KEY)?.color(opacity) ?: Color.DARKGREY + specularColor = get(SolidMaterial.SPECULAR_COLOR_KEY)?.color(opacity) ?: Color.WHITE } } diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXReferenceFactory.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXReferenceFactory.kt index dc72f7f1..d2863cea 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXReferenceFactory.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXReferenceFactory.kt @@ -2,7 +2,10 @@ package space.kscience.visionforge.solid import javafx.scene.Group import javafx.scene.Node -import space.kscience.dataforge.names.* +import space.kscience.dataforge.names.Name +import space.kscience.dataforge.names.cutFirst +import space.kscience.dataforge.names.firstOrNull +import space.kscience.dataforge.names.isEmpty import space.kscience.visionforge.Vision import space.kscience.visionforge.onPropertyChange import kotlin.reflect.KClass @@ -16,7 +19,7 @@ class FXReferenceFactory(val plugin: FX3DPlugin) : FX3DFactory if (name.firstOrNull()?.body == SolidReferenceGroup.REFERENCE_CHILD_PROPERTY_PREFIX) { - val childName = name.firstOrNull()?.index?.toName() ?: error("Wrong syntax for reference child property: '$name'") + val childName = name.firstOrNull()?.index?.let(Name::parse) ?: error("Wrong syntax for reference child property: '$name'") val propertyName = name.cutFirst() val referenceChild = obj[childName] ?: error("Reference child with name '$childName' not found") val child = node.findChild(childName) ?: error("Object child with name '$childName' not found") diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/VisualObjectFXBinding.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/VisualObjectFXBinding.kt index 5045a375..492356ea 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/VisualObjectFXBinding.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/VisualObjectFXBinding.kt @@ -5,7 +5,6 @@ import javafx.beans.binding.* import space.kscience.dataforge.meta.* import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.startsWith -import space.kscience.dataforge.names.toName import space.kscience.dataforge.values.Value import space.kscience.visionforge.Vision import space.kscience.visionforge.onPropertyChange @@ -15,7 +14,7 @@ import tornadofx.* * A caching binding collection for [Vision] properties */ public class VisualObjectFXBinding(public val fx: FX3DPlugin, public val obj: Vision) { - private val bindings = HashMap>() + private val bindings = HashMap>() init { obj.onPropertyChange(fx.context) { name -> @@ -33,30 +32,29 @@ public class VisualObjectFXBinding(public val fx: FX3DPlugin, public val obj: Vi } } - public operator fun get(key: Name): ObjectBinding { + public operator fun get(key: Name): ObjectBinding { return bindings.getOrPut(key) { - object : ObjectBinding() { - override fun computeValue(): MetaItem? = obj.getProperty(key) + object : ObjectBinding() { + override fun computeValue(): Meta? = obj.getProperty(key) } } } - public operator fun get(key: String) = get(key.toName()) + public operator fun get(key: String): ObjectBinding = get(Name.parse(key)) } -public fun ObjectBinding.value(): Binding = objectBinding { it.value } -public fun ObjectBinding.string(): StringBinding = stringBinding { it.string } -public fun ObjectBinding.number(): Binding = objectBinding { it.number } -public fun ObjectBinding.double(): Binding = objectBinding { it.double } -public fun ObjectBinding.float(): Binding = objectBinding { it.float } -public fun ObjectBinding.int(): Binding = objectBinding { it.int } -public fun ObjectBinding.long(): Binding = objectBinding { it.long } -public fun ObjectBinding.node(): Binding = objectBinding { it.node } +public fun ObjectBinding.value(): Binding = objectBinding { it?.value } +public fun ObjectBinding.string(): StringBinding = stringBinding { it.string } +public fun ObjectBinding.number(): Binding = objectBinding { it.number } +public fun ObjectBinding.double(): Binding = objectBinding { it.double } +public fun ObjectBinding.float(): Binding = objectBinding { it.float } +public fun ObjectBinding.int(): Binding = objectBinding { it.int } +public fun ObjectBinding.long(): Binding = objectBinding { it.long } -public fun ObjectBinding.string(default: String): StringBinding = stringBinding { it.string ?: default } -public fun ObjectBinding.double(default: Double): DoubleBinding = doubleBinding { it.double ?: default } -public fun ObjectBinding.float(default: Float): FloatBinding = floatBinding { it.float ?: default } -public fun ObjectBinding.int(default: Int): IntegerBinding = integerBinding { it.int ?: default } -public fun ObjectBinding.long(default: Long): LongBinding = longBinding { it.long ?: default } +public fun ObjectBinding.string(default: String): StringBinding = stringBinding { it.string ?: default } +public fun ObjectBinding.double(default: Double): DoubleBinding = doubleBinding { it.double ?: default } +public fun ObjectBinding.float(default: Float): FloatBinding = floatBinding { it.float ?: default } +public fun ObjectBinding.int(default: Int): IntegerBinding = integerBinding { it.int ?: default } +public fun ObjectBinding.long(default: Long): LongBinding = longBinding { it.long ?: default } -public fun ObjectBinding.transform(transform: (MetaItem) -> T): Binding = objectBinding { it?.let(transform) } +public fun ObjectBinding.transform(transform: (Meta) -> T): Binding = objectBinding { it?.let(transform) } diff --git a/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/GdmlTransformer.kt b/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/GdmlTransformer.kt index 98b4509e..0a837919 100644 --- a/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/GdmlTransformer.kt +++ b/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/GdmlTransformer.kt @@ -1,12 +1,12 @@ package space.kscience.visionforge.gdml import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.meta.MetaBuilder +import space.kscience.dataforge.meta.MutableMeta import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.plus -import space.kscience.dataforge.names.toName + import space.kscience.gdml.* import space.kscience.visionforge.* import space.kscience.visionforge.html.VisionOutput @@ -41,8 +41,8 @@ public class GdmlTransformer { internal val styleCache = HashMap() - public fun Solid.registerAndUseStyle(name: String, builder: MetaBuilder.() -> Unit) { - styleCache.getOrPut(name.toName()) { + public fun Solid.registerAndUseStyle(name: String, builder: MutableMeta.() -> Unit) { + styleCache.getOrPut(Name.parse(name)) { Meta(builder) } useStyle(name) @@ -118,7 +118,7 @@ private class GdmlTransformerEnv(val settings: GdmlTransformer) { private val proto = SolidGroup() private val solids = proto.group(solidsName) { - setProperty("edges.enabled", false) + setPropertyNode("edges.enabled", false) } private val referenceStore = HashMap>() diff --git a/visionforge-gdml/src/commonTest/kotlin/TestCubes.kt b/visionforge-gdml/src/commonTest/kotlin/TestCubes.kt index 9fb80095..0ca76fd1 100644 --- a/visionforge-gdml/src/commonTest/kotlin/TestCubes.kt +++ b/visionforge-gdml/src/commonTest/kotlin/TestCubes.kt @@ -1,7 +1,7 @@ package space.kscience.visionforge.gdml import space.kscience.dataforge.context.Context -import space.kscience.dataforge.names.toName +import space.kscience.dataforge.names.Name import space.kscience.gdml.* import space.kscience.visionforge.Vision import space.kscience.visionforge.get @@ -23,7 +23,7 @@ class TestCubes { fun testCubesDirect() { val vision = cubes.toVision() // println(Solids.encodeToString(vision)) - val smallBoxPrototype = vision.getPrototype("solids.smallBox".toName()) as? Box + val smallBoxPrototype = vision.getPrototype(Name.parse("solids.smallBox")) as? Box assertNotNull(smallBoxPrototype) assertEquals(30.0, smallBoxPrototype.xSize.toDouble()) val smallBoxVision = vision["composite-111.smallBox"]?.unref as? Box @@ -46,7 +46,7 @@ class TestCubes { val vision = cubes.toVision() val serialized = Solids.encodeToString(vision) val deserialized = testContext.visionManager.decodeFromString(serialized) as SolidGroup - val smallBox = deserialized.getPrototype("solids.smallBox".toName()) as? Box + val smallBox = deserialized.getPrototype(Name.parse("solids.smallBox")) as? Box assertNotNull(smallBox) assertEquals(30.0, smallBox.xSize.toDouble()) //println(testContext.visionManager.encodeToString(deserialized)) diff --git a/visionforge-gdml/src/jvmTest/kotlin/space/kscience/visionforge/gdml/TestConvertor.kt b/visionforge-gdml/src/jvmTest/kotlin/space/kscience/visionforge/gdml/TestConvertor.kt index 730adfa9..a8a078a8 100644 --- a/visionforge-gdml/src/jvmTest/kotlin/space/kscience/visionforge/gdml/TestConvertor.kt +++ b/visionforge-gdml/src/jvmTest/kotlin/space/kscience/visionforge/gdml/TestConvertor.kt @@ -1,7 +1,7 @@ package space.kscience.visionforge.gdml import org.junit.jupiter.api.Test -import space.kscience.dataforge.names.toName +import space.kscience.dataforge.names.Name import space.kscience.gdml.Gdml import space.kscience.gdml.decodeFromStream import space.kscience.visionforge.solid.Solids @@ -23,7 +23,7 @@ class TestConvertor { val stream = javaClass.getResourceAsStream("/gdml/cubes.gdml")!! val gdml = Gdml.decodeFromStream(stream) val vision = gdml.toVision() - assertNotNull(vision.getPrototype("solids.box".toName())) + assertNotNull(vision.getPrototype(Name.parse("solids.box"))) println(Solids.encodeToString(vision)) } diff --git a/visionforge-markdown/src/commonMain/kotlin/space/kscience/visionforge/markup/VisionOfMarkup.kt b/visionforge-markdown/src/commonMain/kotlin/space/kscience/visionforge/markup/VisionOfMarkup.kt index 689ed221..2103078d 100644 --- a/visionforge-markdown/src/commonMain/kotlin/space/kscience/visionforge/markup/VisionOfMarkup.kt +++ b/visionforge-markdown/src/commonMain/kotlin/space/kscience/visionforge/markup/VisionOfMarkup.kt @@ -7,7 +7,7 @@ import kotlinx.serialization.modules.polymorphic import kotlinx.serialization.modules.subclass import space.kscience.dataforge.meta.string import space.kscience.dataforge.names.Name -import space.kscience.dataforge.names.toName +import space.kscience.dataforge.names.asName import space.kscience.visionforge.Vision import space.kscience.visionforge.VisionBase import space.kscience.visionforge.setProperty @@ -27,7 +27,7 @@ public class VisionOfMarkup( } public companion object { - public val CONTENT_PROPERTY_KEY: Name = "content".toName() + public val CONTENT_PROPERTY_KEY: Name = "content".asName() public const val COMMONMARK_FORMAT: String = "markdown.commonmark" public const val GFM_FORMAT: String = "markdown.gfm" } diff --git a/visionforge-plotly/build.gradle.kts b/visionforge-plotly/build.gradle.kts index b62876ab..41e541db 100644 --- a/visionforge-plotly/build.gradle.kts +++ b/visionforge-plotly/build.gradle.kts @@ -2,7 +2,7 @@ plugins { id("ru.mipt.npm.gradle.mpp") } -val plotlyVersion = "0.4.3" +val plotlyVersion = "0.5.0-dev-1" kscience { useSerialization() diff --git a/visionforge-plotly/src/commonMain/kotlin/space/kscience/visionforge/plotly/VisionOfPlotly.kt b/visionforge-plotly/src/commonMain/kotlin/space/kscience/visionforge/plotly/VisionOfPlotly.kt index 8603971e..7b2f1c25 100644 --- a/visionforge-plotly/src/commonMain/kotlin/space/kscience/visionforge/plotly/VisionOfPlotly.kt +++ b/visionforge-plotly/src/commonMain/kotlin/space/kscience/visionforge/plotly/VisionOfPlotly.kt @@ -2,6 +2,8 @@ package space.kscience.visionforge.plotly import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +import space.kscience.dataforge.meta.MutableMeta +import space.kscience.dataforge.meta.asObservable import space.kscience.dataforge.misc.DFExperimental import space.kscience.plotly.Plot import space.kscience.plotly.Plotly @@ -13,10 +15,10 @@ import space.kscience.visionforge.root @SerialName("vision.plotly") public class VisionOfPlotly private constructor() : VisionBase() { public constructor(plot: Plot) : this() { - properties = plot.config + properties = plot.meta } - public val plot: Plot get() = Plot(properties ?: Config()) + public val plot: Plot get() = Plot(properties?.asObservable() ?: MutableMeta()) } public fun Plot.asVision(): VisionOfPlotly = VisionOfPlotly(this) diff --git a/visionforge-plotly/src/jsMain/kotlin/space/kscience/visionforge/plotly/plotlyJs.kt b/visionforge-plotly/src/jsMain/kotlin/space/kscience/visionforge/plotly/plotlyJs.kt index be9e1e08..21b447ef 100644 --- a/visionforge-plotly/src/jsMain/kotlin/space/kscience/visionforge/plotly/plotlyJs.kt +++ b/visionforge-plotly/src/jsMain/kotlin/space/kscience/visionforge/plotly/plotlyJs.kt @@ -31,7 +31,7 @@ public actual class PlotlyPlugin : VisionPlugin(), ElementVisionRenderer { override fun render(element: Element, vision: Vision, meta: Meta) { val plot = (vision as? VisionOfPlotly)?.plot ?: error("VisionOfPlotly expected but ${vision::class} found") val config = PlotlyConfig.read(meta) - println(plot.config) + println(plot.meta) println(plot.data[0].toMeta()) element.plot(plot, config) } diff --git a/visionforge-server/src/main/kotlin/space/kscience/visionforge/three/server/VisionServer.kt b/visionforge-server/src/main/kotlin/space/kscience/visionforge/three/server/VisionServer.kt index fabb0675..0a9c60e3 100644 --- a/visionforge-server/src/main/kotlin/space/kscience/visionforge/three/server/VisionServer.kt +++ b/visionforge-server/src/main/kotlin/space/kscience/visionforge/three/server/VisionServer.kt @@ -30,7 +30,6 @@ import space.kscience.dataforge.context.fetch import space.kscience.dataforge.meta.* import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.names.Name -import space.kscience.dataforge.names.toName import space.kscience.visionforge.Vision import space.kscience.visionforge.VisionChange import space.kscience.visionforge.VisionManager @@ -54,12 +53,12 @@ public class VisionServer internal constructor( private val application: Application, private val rootRoute: String, ) : Configurable, CoroutineScope by application { - override val config: ObservableMeta = ObservableMeta() - public var updateInterval: Long by config.long(300, key = UPDATE_INTERVAL_KEY) - public var cacheFragments: Boolean by config.boolean(true) - public var dataEmbed: Boolean by config.boolean(true, "data.embed".toName()) - public var dataFetch: Boolean by config.boolean(false, "data.fetch".toName()) - public var dataConnect: Boolean by config.boolean(true, "data.connect".toName()) + override val meta: ObservableMutableMeta = MutableMeta() + public var updateInterval: Long by meta.long(300, key = UPDATE_INTERVAL_KEY) + public var cacheFragments: Boolean by meta.boolean(true) + public var dataEmbed: Boolean by meta.boolean(true, Name.parse("data.embed")) + public var dataFetch: Boolean by meta.boolean(false, Name.parse("data.fetch")) + public var dataConnect: Boolean by meta.boolean(true, Name.parse("data.connect")) /** * a list of headers that should be applied to all pages @@ -131,14 +130,14 @@ public class VisionServer internal constructor( ?: error("Vision name is not defined in parameters") application.log.debug("Opened server socket for $name") - val vision: Vision = visions[name.toName()] ?: error("Plot with id='$name' not registered") + val vision: Vision = visions[Name.parse(name)] ?: error("Plot with id='$name' not registered") launch { incoming.consumeEach { val change = visionManager.jsonFormat.decodeFromString( VisionChange.serializer(), it.data.decodeToString() ) - vision.update(change) + vision.change(change) } } @@ -161,7 +160,7 @@ public class VisionServer internal constructor( val name: String = call.request.queryParameters["name"] ?: error("Vision name is not defined in parameters") - val vision: Vision? = visions[name.toName()] + val vision: Vision? = visions[Name.parse(name)] if (vision == null) { call.respond(HttpStatusCode.NotFound, "Vision with name '$name' not found") } else { @@ -232,7 +231,7 @@ public class VisionServer internal constructor( public companion object { public const val DEFAULT_PAGE: String = "/" - public val UPDATE_INTERVAL_KEY: Name = "update.interval".toName() + public val UPDATE_INTERVAL_KEY: Name = Name.parse("update.interval") } } diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/ColorAccessor.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/ColorAccessor.kt index 9232874c..66efe69b 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/ColorAccessor.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/ColorAccessor.kt @@ -1,8 +1,6 @@ package space.kscience.visionforge.solid -import space.kscience.dataforge.meta.MutableItemProvider -import space.kscience.dataforge.meta.set -import space.kscience.dataforge.meta.value +import space.kscience.dataforge.meta.* import space.kscience.dataforge.names.Name import space.kscience.dataforge.values.Value import space.kscience.dataforge.values.asValue @@ -11,11 +9,17 @@ import space.kscience.visionforge.Colors import space.kscience.visionforge.VisionBuilder @VisionBuilder -public class ColorAccessor(private val parent: MutableItemProvider, private val colorKey: Name) { +public class ColorAccessor(private val parent: MutableMetaProvider, private val colorKey: Name) { public var value: Value? - get() = parent.getItem(colorKey).value + get() = parent.getMeta(colorKey)?.value set(value) { - parent[colorKey] = value + parent.setValue(colorKey,value) + } + + public var item: Meta? + get() = parent.getMeta(colorKey) + set(value) { + parent.setMeta(colorKey,value) } } diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Composite.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Composite.kt index d1d41d09..4017b11b 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Composite.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Composite.kt @@ -31,7 +31,7 @@ public inline fun VisionContainerBuilder.composite( if (children.size != 2) error("Composite requires exactly two children") return Composite(type, children[0], children[1]).also { composite -> composite.configure { - update(group.meta) + update(group.meta()) } if (group.position != null) { composite.position = group.position diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Extruded.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Extruded.kt index d94062d8..0ecc02b0 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Extruded.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Extruded.kt @@ -2,7 +2,8 @@ package space.kscience.visionforge.solid import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import space.kscience.dataforge.meta.ObservableMeta +import space.kscience.dataforge.meta.MutableMeta +import space.kscience.dataforge.meta.ObservableMutableMeta import space.kscience.visionforge.* import kotlin.math.PI import kotlin.math.cos @@ -95,7 +96,7 @@ public class Extruded( public class ExtrudeBuilder( public var shape: List = emptyList(), public var layers: ArrayList = ArrayList(), - config: ObservableMeta = ObservableMeta() + config: ObservableMutableMeta = MutableMeta() ) : SimpleVisionPropertyContainer(config) { public fun shape(block: Shape2DBuilder.() -> Unit) { this.shape = Shape2DBuilder().apply(block).build() @@ -105,7 +106,7 @@ public class ExtrudeBuilder( layers.add(Layer(x.toFloat(), y.toFloat(), z.toFloat(), scale.toFloat())) } - internal fun build(): Extruded = Extruded(shape, layers).apply { configure(config) } + internal fun build(): Extruded = Extruded(shape, layers).apply { configure(meta()) } } @VisionBuilder diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/PolyLine.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/PolyLine.kt index cd9f2cbd..d8aa937c 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/PolyLine.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/PolyLine.kt @@ -2,13 +2,12 @@ package space.kscience.visionforge.solid import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import space.kscience.dataforge.meta.number import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.plus import space.kscience.visionforge.VisionBuilder import space.kscience.visionforge.VisionContainerBuilder -import space.kscience.visionforge.allProperties +import space.kscience.visionforge.numberProperty import space.kscience.visionforge.set @Serializable @@ -16,8 +15,8 @@ import space.kscience.visionforge.set public class PolyLine(public val points: List) : SolidBase(), Solid { //var lineType by string() - public var thickness: Number by allProperties(inherit = false).number(1.0, - key = SolidMaterial.MATERIAL_KEY + THICKNESS_KEY) + public var thickness: Number by numberProperty(name = SolidMaterial.MATERIAL_KEY + THICKNESS_KEY) { 1.0 } + public companion object { public val THICKNESS_KEY: Name = "thickness".asName() diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Solid.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Solid.kt index a4c8e69c..f2ad38fd 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Solid.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Solid.kt @@ -1,7 +1,7 @@ package space.kscience.visionforge.solid import space.kscience.dataforge.meta.* -import space.kscience.dataforge.meta.descriptors.NodeDescriptor +import space.kscience.dataforge.meta.descriptors.* import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.plus @@ -35,7 +35,7 @@ import kotlin.reflect.KProperty */ public interface Solid : Vision { - override val descriptor: NodeDescriptor get() = Companion.descriptor + override val descriptor: MetaDescriptor get() = Companion.descriptor public companion object { // val SELECTED_KEY = "selected".asName() @@ -69,39 +69,38 @@ public interface Solid : Vision { public val Y_SCALE_KEY: Name = SCALE_KEY + Y_KEY public val Z_SCALE_KEY: Name = SCALE_KEY + Z_KEY - public val descriptor: NodeDescriptor by lazy { - NodeDescriptor { - value(VISIBLE_KEY) { + public val descriptor: MetaDescriptor by lazy { + MetaDescriptor { + value(VISIBLE_KEY, ValueType.BOOLEAN) { inherited = false - type(ValueType.BOOLEAN) default(true) } //TODO replace by descriptor merge - value(Vision.STYLE_KEY) { - type(ValueType.STRING) + value(Vision.STYLE_KEY, ValueType.STRING) { multiple = true hide() } - node(POSITION_KEY){ + node(POSITION_KEY) { hide() } - node(ROTATION_KEY){ + node(ROTATION_KEY) { hide() } - node(SCALE_KEY){ + node(SCALE_KEY) { hide() } - value(DETAIL_KEY) { - type(ValueType.NUMBER) + value(DETAIL_KEY, ValueType.NUMBER) { hide() } - item(SolidMaterial.MATERIAL_KEY.toString(), SolidMaterial.descriptor) + item(SolidMaterial.MATERIAL_KEY.toString(), SolidMaterial){ + valueRequirement = ValueRequirement.ABSENT + } enum(ROTATION_ORDER_KEY, default = RotationOrder.XYZ) { hide() @@ -115,7 +114,7 @@ public interface Solid : Vision { * Get the layer number this solid belongs to. Return 0 if layer is not defined. */ public var Solid.layer: Int - get() = allProperties().getItem(LAYER_KEY).int ?: 0 + get() = getProperty(LAYER_KEY, inherit = true).int ?: 0 set(value) { setProperty(LAYER_KEY, value) } @@ -136,7 +135,7 @@ public enum class RotationOrder { */ public var Solid.rotationOrder: RotationOrder get() = getProperty(Solid.ROTATION_ORDER_KEY).enum() ?: RotationOrder.XYZ - set(value) = setProperty(Solid.ROTATION_ORDER_KEY, value.name.asValue()) + set(value) = setPropertyValue(Solid.ROTATION_ORDER_KEY, value.name.asValue()) /** @@ -144,7 +143,7 @@ public var Solid.rotationOrder: RotationOrder */ public var Solid.detail: Int? get() = getProperty(DETAIL_KEY, false).int - set(value) = setProperty(DETAIL_KEY, value?.asValue()) + set(value) = setPropertyValue(DETAIL_KEY, value?.asValue()) /** * If this property is true, the object will be ignored on render. @@ -152,7 +151,7 @@ public var Solid.detail: Int? */ public var Vision.ignore: Boolean? get() = getProperty(IGNORE_KEY, false).boolean - set(value) = setProperty(IGNORE_KEY, value?.asValue()) + set(value) = setPropertyValue(IGNORE_KEY, value?.asValue()) //var VisualObject.selected: Boolean? // get() = getProperty(SELECTED_KEY).boolean @@ -182,7 +181,7 @@ internal fun point(name: Name, default: Float): ReadWriteProperty, value: Point3D?) { if (value == null) { - thisRef.setProperty(name, null) + thisRef.setPropertyNode(name, null) } else { thisRef.setProperty(name + X_KEY, value.x) thisRef.setProperty(name + Y_KEY, value.y) diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidBase.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidBase.kt index d2fee33a..2beee1ed 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidBase.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidBase.kt @@ -2,17 +2,17 @@ package space.kscience.visionforge.solid import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import space.kscience.dataforge.meta.descriptors.NodeDescriptor +import space.kscience.dataforge.meta.descriptors.MetaDescriptor import space.kscience.visionforge.VisionBase import space.kscience.visionforge.VisionChange @Serializable @SerialName("solid") public open class SolidBase : VisionBase(), Solid { - override val descriptor: NodeDescriptor get() = Solid.descriptor + override val descriptor: MetaDescriptor get() = Solid.descriptor - override fun update(change: VisionChange) { + override fun change(change: VisionChange) { updatePosition(change.properties) - super.update(change) + super.change(change) } } diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidGroup.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidGroup.kt index b78ec4a5..bc6676f6 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidGroup.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidGroup.kt @@ -2,7 +2,7 @@ package space.kscience.visionforge.solid import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import space.kscience.dataforge.meta.descriptors.NodeDescriptor +import space.kscience.dataforge.meta.descriptors.MetaDescriptor import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.NameToken import space.kscience.visionforge.* @@ -40,7 +40,7 @@ public class SolidGroup : VisionGroupBase(), Solid, PrototypeHolder { } - override val descriptor: NodeDescriptor get() = Solid.descriptor + override val descriptor: MetaDescriptor get() = Solid.descriptor /** * Get a prototype redirecting the request to the parent if prototype is not found. @@ -60,9 +60,9 @@ public class SolidGroup : VisionGroupBase(), Solid, PrototypeHolder { override fun createGroup(): SolidGroup = SolidGroup() - override fun update(change: VisionChange) { + override fun change(change: VisionChange) { updatePosition(change.properties) - super.update(change) + super.change(change) } public companion object { diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidMaterial.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidMaterial.kt index aa6c9301..3d9843a3 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidMaterial.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidMaterial.kt @@ -1,8 +1,8 @@ package space.kscience.visionforge.solid import space.kscience.dataforge.meta.* -import space.kscience.dataforge.meta.descriptors.NodeDescriptor -import space.kscience.dataforge.meta.descriptors.attributes +import space.kscience.dataforge.meta.descriptors.MetaDescriptor +import space.kscience.dataforge.meta.descriptors.value import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.plus @@ -26,6 +26,8 @@ public class SolidMaterial : Scheme() { */ public val specularColor: ColorAccessor = ColorAccessor(this, SPECULAR_COLOR_KEY) + public val emissiveColor: ColorAccessor = ColorAccessor(this, "emissiveColor".asName()) + /** * Opacity */ @@ -48,43 +50,37 @@ public class SolidMaterial : Scheme() { public val WIREFRAME_KEY: Name = "wireframe".asName() public val MATERIAL_WIREFRAME_KEY: Name = MATERIAL_KEY + WIREFRAME_KEY - public override val descriptor: NodeDescriptor by lazy { + public override val descriptor: MetaDescriptor by lazy { //must be lazy to avoid initialization bug - NodeDescriptor { + MetaDescriptor { inherited = true usesStyles = true - value(COLOR_KEY) { + value(COLOR_KEY, ValueType.STRING, ValueType.NUMBER) { inherited = true usesStyles = true - type(ValueType.STRING, ValueType.NUMBER) widgetType = "color" } - value(SPECULAR_COLOR_KEY) { + value(SPECULAR_COLOR_KEY, ValueType.STRING, ValueType.NUMBER) { inherited = true usesStyles = true - type(ValueType.STRING, ValueType.NUMBER) widgetType = "color" hide() } - value(OPACITY_KEY) { + value(OPACITY_KEY, ValueType.NUMBER) { inherited = true usesStyles = true - type(ValueType.NUMBER) default(1.0) - attributes { - this["min"] = 0.0 - this["max"] = 1.0 - this["step"] = 0.1 - } + attributes["min"] = 0.0 + attributes["max"] = 1.0 + attributes["step"] = 0.1 widgetType = "slider" } - value(WIREFRAME_KEY) { + value(WIREFRAME_KEY, ValueType.BOOLEAN) { inherited = true usesStyles = true - type(ValueType.BOOLEAN) default(false) } } @@ -94,21 +90,23 @@ public class SolidMaterial : Scheme() { public val Solid.color: ColorAccessor get() = ColorAccessor( - allProperties(inherit = true), + meta(inherit = true), MATERIAL_COLOR_KEY ) public var Solid.material: SolidMaterial? - get() = getProperty(MATERIAL_KEY, inherit = true).node?.let { SolidMaterial.read(it) } - set(value) = setProperty(MATERIAL_KEY, value?.rootNode) + get() = getProperty(MATERIAL_KEY, inherit = true)?.let { SolidMaterial.read(it) } + set(value) = setPropertyNode(MATERIAL_KEY, value?.meta) @VisionBuilder public fun Solid.material(builder: SolidMaterial.() -> Unit) { - ownProperties.getChild(MATERIAL_KEY).update(SolidMaterial, builder) + configure(MATERIAL_KEY){ + updateWith(SolidMaterial,builder) + } } public var Solid.opacity: Number? get() = getProperty(MATERIAL_OPACITY_KEY, inherit = true).number set(value) { - setProperty(MATERIAL_OPACITY_KEY, value?.asValue()) + setPropertyValue(MATERIAL_OPACITY_KEY, value?.asValue()) } \ No newline at end of file diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidReference.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidReference.kt index 1cb5eac8..0149fc88 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidReference.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidReference.kt @@ -4,11 +4,11 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.mapNotNull import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import space.kscience.dataforge.meta.MetaItem -import space.kscience.dataforge.meta.asMetaItem -import space.kscience.dataforge.meta.descriptors.NodeDescriptor +import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.descriptors.MetaDescriptor import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.names.* +import space.kscience.dataforge.values.Value import space.kscience.visionforge.* @@ -37,7 +37,7 @@ private fun SolidReference.getRefProperty( inherit: Boolean, includeStyles: Boolean, includeDefaults: Boolean, -): MetaItem? = if (!inherit && !includeStyles && !includeDefaults) { +): Meta? = if (!inherit && !includeStyles && !includeDefaults) { getOwnProperty(name) } else { buildList { @@ -88,9 +88,9 @@ public class SolidReferenceGroup( inherit: Boolean, includeStyles: Boolean, includeDefaults: Boolean, - ): MetaItem? = getRefProperty(name, inherit, includeStyles, includeDefaults) + ): Meta? = getRefProperty(name, inherit, includeStyles, includeDefaults) - override val descriptor: NodeDescriptor get() = prototype.descriptor + override val descriptor: MetaDescriptor get() = prototype.descriptor /** @@ -118,11 +118,15 @@ public class SolidReferenceGroup( ReferenceChild(owner, refName + key.asName()) } ?: emptyMap() - override fun getOwnProperty(name: Name): MetaItem? = + override fun getOwnProperty(name: Name): Meta? = owner.getOwnProperty(childPropertyName(refName, name)) - override fun setProperty(name: Name, item: MetaItem?, notify: Boolean) { - owner.setProperty(childPropertyName(refName, name), item, notify) + override fun setPropertyNode(name: Name, node: Meta?, notify: Boolean) { + owner.setPropertyNode(childPropertyName(refName, name), node, notify) + } + + override fun setPropertyValue(name: Name, value: Value?, notify: Boolean) { + owner.setPropertyValue(childPropertyName(refName, name), value, notify) } override fun getProperty( @@ -130,7 +134,7 @@ public class SolidReferenceGroup( inherit: Boolean, includeStyles: Boolean, includeDefaults: Boolean, - ): MetaItem? = getRefProperty(name, inherit, includeStyles, includeDefaults) + ): Meta? = getRefProperty(name, inherit, includeStyles, includeDefaults) override var parent: VisionGroup? get() { @@ -155,13 +159,13 @@ public class SolidReferenceGroup( owner.invalidateProperty(childPropertyName(refName, propertyName)) } - override fun update(change: VisionChange) { + override fun change(change: VisionChange) { change.properties?.let { - updateProperties(Name.EMPTY, it.asMetaItem()) + updateProperties(Name.EMPTY, it) } } - override val descriptor: NodeDescriptor get() = prototype.descriptor + override val descriptor: MetaDescriptor get() = prototype.descriptor } @@ -184,7 +188,7 @@ public fun SolidGroup.ref( public fun SolidGroup.ref( name: String, obj: Solid, - templateName: Name = name.toName(), + templateName: Name = Name.parse(name), ): SolidReferenceGroup { val existing = getPrototype(templateName) if (existing == null) { diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/geometry.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/geometry.kt index f5aa2d4a..31611dc9 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/geometry.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/geometry.kt @@ -95,13 +95,13 @@ public fun MutablePoint3D.normalizeInPlace() { z /= norm } -internal fun ItemProvider.point3D(default: Float = 0f) = object : Point3D { +internal fun MetaProvider.point3D(default: Float = 0f) = object : Point3D { override val x: Float by float(default) override val y: Float by float(default) override val z: Float by float(default) } -public fun Point3D.toMeta(): MetaBuilder = Meta { +public fun Point3D.toMeta(): Meta = Meta { X_KEY put x Y_KEY put y Z_KEY put z @@ -115,7 +115,7 @@ internal fun Meta.toVector(default: Float = 0f) = Point3D( ) internal fun Solid.updatePosition(meta: Meta?) { - meta[Solid.POSITION_KEY].node?.toVector()?.let { position = it } - meta[Solid.ROTATION_KEY].node?.toVector()?.let { rotation = it } - meta[Solid.SCALE_KEY].node?.toVector(1f)?.let { scale = it } + meta?.get(Solid.POSITION_KEY)?.toVector()?.let { position = it } + meta?.get(Solid.ROTATION_KEY)?.toVector()?.let { rotation = it } + meta?.get(Solid.SCALE_KEY)?.toVector(1f)?.let { scale = it } } \ No newline at end of file diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Axes.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Axes.kt index 2f3e1645..8f980dad 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Axes.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Axes.kt @@ -3,7 +3,7 @@ package space.kscience.visionforge.solid.specifications import space.kscience.dataforge.meta.Scheme import space.kscience.dataforge.meta.SchemeSpec import space.kscience.dataforge.meta.boolean -import space.kscience.dataforge.meta.descriptors.NodeDescriptor +import space.kscience.dataforge.meta.descriptors.MetaDescriptor import space.kscience.dataforge.meta.double import space.kscience.visionforge.value @@ -16,8 +16,8 @@ public class Axes : Scheme() { public const val AXIS_SIZE: Double = 1000.0 public const val AXIS_WIDTH: Double = 3.0 - override val descriptor: NodeDescriptor by lazy { - NodeDescriptor { + override val descriptor: MetaDescriptor by lazy { + MetaDescriptor { value(Axes::visible){ default(false) } diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Camera.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Camera.kt index 905fa234..2319fa69 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Camera.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Camera.kt @@ -2,7 +2,7 @@ package space.kscience.visionforge.solid.specifications import space.kscience.dataforge.meta.Scheme import space.kscience.dataforge.meta.SchemeSpec -import space.kscience.dataforge.meta.descriptors.NodeDescriptor +import space.kscience.dataforge.meta.descriptors.MetaDescriptor import space.kscience.dataforge.meta.double import space.kscience.dataforge.meta.int import space.kscience.visionforge.value @@ -27,8 +27,8 @@ public class Camera : Scheme() { public const val FAR_CLIP: Double = 10000.0 public const val FIELD_OF_VIEW: Int = 75 - override val descriptor: NodeDescriptor by lazy { - NodeDescriptor { + override val descriptor: MetaDescriptor by lazy { + MetaDescriptor { value(Camera::fov){ default(FIELD_OF_VIEW) } diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Canvas3DOptions.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Canvas3DOptions.kt index 11dd5800..ea546821 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Canvas3DOptions.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Canvas3DOptions.kt @@ -1,8 +1,7 @@ package space.kscience.visionforge.solid.specifications import space.kscience.dataforge.meta.* -import space.kscience.dataforge.meta.descriptors.NodeDescriptor -import space.kscience.dataforge.meta.descriptors.attributes +import space.kscience.dataforge.meta.descriptors.MetaDescriptor import space.kscience.dataforge.names.Name import space.kscience.dataforge.values.ValueType import space.kscience.visionforge.hide @@ -16,30 +15,26 @@ public class Clipping : Scheme() { public var z: Double? by double() public companion object : SchemeSpec(::Clipping) { - override val descriptor: NodeDescriptor = NodeDescriptor { + override val descriptor: MetaDescriptor = MetaDescriptor { value(Clipping::x) { widgetType = "range" - attributes { - set("min", 0.0) - set("max", 1.0) - set("step", 0.01) - } + attributes["min"] = 0.0 + attributes["max"] = 1.0 + attributes["step"] = 0.01 } value(Clipping::y) { widgetType = "range" - attributes { - set("min", 0.0) - set("max", 1.0) - set("step", 0.01) - } + attributes["min"] = 0.0 + attributes["max"] = 1.0 + attributes["step"] = 0.01 + } value(Clipping::z) { widgetType = "range" - attributes { - set("min", 0.0) - set("max", 1.0) - set("step", 0.01) - } + attributes["min"] = 0.0 + attributes["max"] = 1.0 + attributes["step"] = 0.01 + } } } @@ -55,7 +50,7 @@ public class CanvasSize : Scheme() { public var maxHeight: Number by number { maxSize } public companion object : SchemeSpec(::CanvasSize) { - override val descriptor: NodeDescriptor = NodeDescriptor { + override val descriptor: MetaDescriptor = MetaDescriptor { value(CanvasSize::minSize) value(CanvasSize::minWith) value(CanvasSize::minHeight) @@ -82,8 +77,8 @@ public class Canvas3DOptions : Scheme() { public companion object : SchemeSpec(::Canvas3DOptions) { - override val descriptor: NodeDescriptor by lazy { - NodeDescriptor { + override val descriptor: MetaDescriptor by lazy { + MetaDescriptor { scheme(Canvas3DOptions::axes, Axes) scheme(Canvas3DOptions::light, Light) @@ -104,7 +99,7 @@ public class Canvas3DOptions : Scheme() { multiple = true default(listOf(0)) widgetType = "multiSelect" - allow(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + allowedValues(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10) } scheme(Canvas3DOptions::clipping, Clipping) } diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/transform/RemoveSingleChild.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/transform/RemoveSingleChild.kt index a51aaa25..e623ef46 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/transform/RemoveSingleChild.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/transform/RemoveSingleChild.kt @@ -1,12 +1,9 @@ package space.kscience.visionforge.solid.transform -import space.kscience.dataforge.meta.itemSequence +import space.kscience.dataforge.meta.update import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.names.asName -import space.kscience.visionforge.MutableVisionGroup -import space.kscience.visionforge.Vision -import space.kscience.visionforge.VisionGroup -import space.kscience.visionforge.meta +import space.kscience.visionforge.* import space.kscience.visionforge.solid.* private operator fun Number.plus(other: Number) = toFloat() + other.toFloat() @@ -24,10 +21,8 @@ internal fun Vision.updateFrom(other: Vision): Vision { scaleX *= other.scaleX scaleY *= other.scaleY scaleZ *= other.scaleZ - other.meta.itemSequence().forEach { (name, item) -> - if (getProperty(name) == null) { - setProperty(name, item) - } + configure{ + update(other.meta()) } } return this diff --git a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/ConvexTest.kt b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/ConvexTest.kt index 58cde48e..a7133006 100644 --- a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/ConvexTest.kt +++ b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/ConvexTest.kt @@ -1,9 +1,8 @@ package space.kscience.visionforge.solid -import space.kscience.dataforge.meta.MetaItemNode import space.kscience.dataforge.meta.getIndexed import space.kscience.dataforge.meta.node -import space.kscience.dataforge.meta.toMetaItem +import space.kscience.dataforge.meta.toMeta import space.kscience.dataforge.misc.DFExperimental import kotlin.test.Test import kotlin.test.assertEquals @@ -29,9 +28,9 @@ class ConvexTest { val convex = group.children.values.first() as Convex val json = Solids.jsonForSolids.encodeToJsonElement(Convex.serializer(), convex) - val meta = json.toMetaItem().node!! + val meta = json.toMeta() - val points = meta.getIndexed("points").values.map { (it as MetaItemNode<*>).node.point3D() } + val points = meta.getIndexed("points").values.map { it.point3D() } assertEquals(8, points.count()) assertEquals(8, convex.points.size) diff --git a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/DescriptorTest.kt b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/DescriptorTest.kt index 95f6b23e..6f004f5c 100644 --- a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/DescriptorTest.kt +++ b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/DescriptorTest.kt @@ -1,6 +1,5 @@ package space.kscience.visionforge.solid -import space.kscience.dataforge.meta.descriptors.ValueDescriptor import space.kscience.dataforge.meta.descriptors.get import space.kscience.dataforge.values.ValueType import space.kscience.visionforge.solid.specifications.Canvas3DOptions @@ -16,7 +15,7 @@ class DescriptorTest { val axesSize = descriptor["axes.size"] assertNotNull(axesSize) assertTrue { - ValueType.NUMBER in (axesSize as ValueDescriptor).type!! + ValueType.NUMBER in axesSize.valueTypes!! } } } \ No newline at end of file diff --git a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/PropertyTest.kt b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/PropertyTest.kt index 1a9aa45b..a31ed9f8 100644 --- a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/PropertyTest.kt +++ b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/PropertyTest.kt @@ -12,7 +12,7 @@ class PropertyTest { fun testInheritedProperty() { var box: Box? = null val group = SolidGroup().apply { - setProperty("test", 22) + setPropertyNode("test", 22) group { box = box(100, 100, 100) } diff --git a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/SerializationTest.kt b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/SerializationTest.kt index 6df7b285..d878881f 100644 --- a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/SerializationTest.kt +++ b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/SerializationTest.kt @@ -1,7 +1,6 @@ package space.kscience.visionforge.solid import space.kscience.dataforge.names.Name -import space.kscience.dataforge.names.toName import space.kscience.visionforge.MutableVisionGroup import space.kscience.visionforge.get import space.kscience.visionforge.meta @@ -14,7 +13,7 @@ import kotlin.test.assertEquals */ fun SolidGroup.refGroup( name: String, - templateName: Name = name.toName(), + templateName: Name = Name.parse(name), block: MutableVisionGroup.() -> Unit ): SolidReferenceGroup { val group = SolidGroup().apply(block) @@ -30,10 +29,10 @@ class SerializationTest { x = 100 z = -100 } - val string = Solids.encodeToString(cube) + val string = Solids.encodeToString(cube) println(string) val newCube = Solids.decodeFromString(string) - assertEquals(cube.meta, newCube.meta) + assertEquals(cube.meta(), newCube.meta()) } @Test @@ -43,10 +42,10 @@ class SerializationTest { x = 100 z = -100 } - val group = SolidGroup{ + val group = SolidGroup { ref("cube", cube) - refGroup("pg", "pg.content".toName()){ - sphere(50){ + refGroup("pg", Name.parse("pg.content")) { + sphere(50) { x = -100 } } @@ -54,7 +53,7 @@ class SerializationTest { val string = Solids.encodeToString(group) println(string) val reconstructed = Solids.decodeFromString(string) as SolidGroup - assertEquals(group["cube"]?.meta, reconstructed["cube"]?.meta) + assertEquals(group["cube"]?.meta(), reconstructed["cube"]?.meta()) } } \ No newline at end of file diff --git a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/VisionUpdateTest.kt b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/VisionUpdateTest.kt index 2e87adf8..821e7f04 100644 --- a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/VisionUpdateTest.kt +++ b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/VisionUpdateTest.kt @@ -2,8 +2,9 @@ package space.kscience.visionforge.solid import space.kscience.dataforge.context.Global import space.kscience.dataforge.context.fetch -import space.kscience.dataforge.meta.MetaItem -import space.kscience.dataforge.names.toName +import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.names.asName +import space.kscience.dataforge.values.asValue import space.kscience.visionforge.VisionChange import space.kscience.visionforge.get import kotlin.test.Test @@ -24,10 +25,10 @@ class VisionUpdateTest { color(123) box(100,100,100) } - propertyChanged("top".toName(), SolidMaterial.MATERIAL_COLOR_KEY, MetaItem.of("red")) - propertyChanged("origin".toName(), SolidMaterial.MATERIAL_COLOR_KEY, MetaItem.of("red")) + propertyChanged("top".asName(), SolidMaterial.MATERIAL_COLOR_KEY, Meta("red".asValue())) + propertyChanged("origin".asName(), SolidMaterial.MATERIAL_COLOR_KEY, Meta("red".asValue())) } - targetVision.update(dif) + targetVision.change(dif) assertTrue { targetVision["top"] is SolidGroup } assertEquals("red", (targetVision["origin"] as Solid).color.string) // Should work assertEquals("#00007b", (targetVision["top"] as Solid).color.string) // new item always takes precedence @@ -40,8 +41,8 @@ class VisionUpdateTest { color(123) box(100,100,100) } - propertyChanged("top".toName(), SolidMaterial.MATERIAL_COLOR_KEY, MetaItem.of("red")) - propertyChanged("origin".toName(), SolidMaterial.MATERIAL_COLOR_KEY, MetaItem.of("red")) + propertyChanged("top".asName(), SolidMaterial.MATERIAL_COLOR_KEY, Meta("red".asValue())) + propertyChanged("origin".asName(), SolidMaterial.MATERIAL_COLOR_KEY, Meta("red".asValue())) } val serialized = visionManager.jsonFormat.encodeToString(VisionChange.serializer(), change) println(serialized) diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/MeshThreeFactory.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/MeshThreeFactory.kt index 7cbd4d3d..7e90a657 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/MeshThreeFactory.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/MeshThreeFactory.kt @@ -90,7 +90,7 @@ public fun Mesh.applyEdges(obj: Solid) { MeshThreeFactory.EDGES_MATERIAL_KEY, inherit = true, includeStyles = true - ).node, + ), true ) if (edges == null) { diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCanvas.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCanvas.kt index 3afb6776..2cfcadf5 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCanvas.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCanvas.kt @@ -168,10 +168,10 @@ public class ThreeCanvas( } //Clipping planes - options.onChange(this@ThreeCanvas) { name, _, _ -> + options.meta.onChange(this@ThreeCanvas) { name-> if (name.startsWith(Canvas3DOptions::clipping.name.asName())) { val clipping = options.clipping - if (!clipping.isEmpty()) { + if (!clipping.meta.isEmpty()) { renderer.localClippingEnabled = true boundingBox?.let { boundingBox -> val xClippingPlane = clipping.x?.let { @@ -212,9 +212,9 @@ public class ThreeCanvas( private fun Object3D.fullName(): Name { if (root == null) error("Can't resolve element name without the root") return if (parent == root) { - name.toName() + Name.parse(name) } else { - (parent?.fullName() ?: Name.EMPTY) + name.toName() + (parent?.fullName() ?: Name.EMPTY) + Name.parse(name) } } @@ -237,7 +237,7 @@ public class ThreeCanvas( private fun buildLight(spec: Light?): info.laht.threekt.lights.Light = AmbientLight(0x404040) private fun addControls(element: Node, controls: Controls) { - when (controls["type"].string) { + when (controls.meta["type"].string) { "trackball" -> TrackballControls(camera, element) else -> OrbitControls(camera, element) } diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeLineFactory.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeLineFactory.kt index b2858fdb..bc369086 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeLineFactory.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeLineFactory.kt @@ -4,7 +4,6 @@ import info.laht.threekt.core.BufferGeometry import info.laht.threekt.core.Object3D import info.laht.threekt.math.Color import info.laht.threekt.objects.LineSegments -import space.kscience.dataforge.meta.node import space.kscience.visionforge.onPropertyChange import space.kscience.visionforge.solid.PolyLine import space.kscience.visionforge.solid.color @@ -20,7 +19,10 @@ public object ThreeLineFactory : ThreeFactory { setFromPoints(Array(obj.points.size) { obj.points[it].toVector() }) } - val material = ThreeMaterials.getLineMaterial(obj.getProperty(MeshThreeFactory.EDGES_MATERIAL_KEY).node, true) + val material = ThreeMaterials.getLineMaterial( + obj.getProperty(MeshThreeFactory.EDGES_MATERIAL_KEY), + true + ) material.linewidth = obj.thickness.toDouble() material.color = obj.color.string?.let { Color(it) } ?: DEFAULT_LINE_COLOR diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeMaterials.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeMaterials.kt index 5f7ef051..258df5ff 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeMaterials.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeMaterials.kt @@ -13,7 +13,7 @@ import space.kscience.dataforge.values.int import space.kscience.dataforge.values.string import space.kscience.visionforge.Colors import space.kscience.visionforge.Vision -import space.kscience.visionforge.ownProperties +import space.kscience.visionforge.meta import space.kscience.visionforge.solid.SolidMaterial @@ -41,7 +41,7 @@ public object ThreeMaterials { private val lineMaterialCache = HashMap() private fun buildLineMaterial(meta: Meta): LineBasicMaterial = LineBasicMaterial().apply { - color = meta[SolidMaterial.COLOR_KEY]?.getColor() ?: DEFAULT_LINE_COLOR + color = meta[SolidMaterial.COLOR_KEY]?.threeColor() ?: DEFAULT_LINE_COLOR opacity = meta[SolidMaterial.OPACITY_KEY].double ?: 1.0 transparent = opacity < 1.0 linewidth = meta["thickness"].double ?: 1.0 @@ -59,11 +59,12 @@ public object ThreeMaterials { private val materialCache = HashMap() internal fun buildMaterial(meta: Meta): Material { + val material = SolidMaterial.read(meta) return meta[SolidMaterial.SPECULAR_COLOR_KEY]?.let { specularColor -> MeshPhongMaterial().apply { - color = meta[SolidMaterial.COLOR_KEY]?.getColor() ?: DEFAULT_COLOR - specular = specularColor.getColor() - emissive = specular + color = meta[SolidMaterial.COLOR_KEY]?.threeColor() ?: DEFAULT_COLOR + specular = specularColor.threeColor() + emissive = material.emissiveColor.item?.threeColor() ?: specular reflectivity = 0.5 refractionRatio = 1.0 shininess = 100.0 @@ -73,7 +74,7 @@ public object ThreeMaterials { needsUpdate = true } } ?: MeshBasicMaterial().apply { - color = meta[SolidMaterial.COLOR_KEY]?.getColor() ?: DEFAULT_COLOR + color = meta[SolidMaterial.COLOR_KEY]?.threeColor() ?: DEFAULT_COLOR opacity = meta[SolidMaterial.OPACITY_KEY]?.double ?: 1.0 transparent = opacity < 1.0 wireframe = meta[SolidMaterial.WIREFRAME_KEY].boolean ?: false @@ -93,22 +94,19 @@ public object ThreeMaterials { /** * Infer color based on meta item */ -public fun MetaItem.getColor(): Color { - return when (this) { - is MetaItemValue -> if (this.value.type == ValueType.NUMBER) { +public fun Meta.threeColor(): Color { + return value?.let { value -> + if (value.type == ValueType.NUMBER) { val int = value.int Color(int) } else { - Color(this.value.string) + Color(value.string) } - is MetaItemNode -> { - Color( - node[Colors.RED_KEY]?.int ?: 0, - node[Colors.GREEN_KEY]?.int ?: 0, - node[Colors.BLUE_KEY]?.int ?: 0 - ) - } - } + } ?: Color( + this[Colors.RED_KEY]?.int ?: 0, + this[Colors.GREEN_KEY]?.int ?: 0, + this[Colors.BLUE_KEY]?.int ?: 0 + ) } private var Material.cached: Boolean @@ -119,7 +117,7 @@ private var Material.cached: Boolean public fun Mesh.updateMaterial(vision: Vision) { //val meta = vision.getProperty(SolidMaterial.MATERIAL_KEY, inherit = true).node - val ownMaterialMeta = vision.ownProperties[SolidMaterial.MATERIAL_KEY] + val ownMaterialMeta = vision.meta()[SolidMaterial.MATERIAL_KEY] val parentMaterialMeta = vision.parent?.getProperty( SolidMaterial.MATERIAL_KEY, inherit = true, @@ -135,7 +133,7 @@ public fun Mesh.updateMaterial(vision: Vision) { inherit = false, includeStyles = true, includeDefaults = false - ).node?.let { + )?.let { ThreeMaterials.cacheMaterial(it) } ?: ThreeMaterials.DEFAULT } @@ -143,7 +141,7 @@ public fun Mesh.updateMaterial(vision: Vision) { vision.getProperty( SolidMaterial.MATERIAL_KEY, inherit = true - ).node?.let { + )?.let { ThreeMaterials.buildMaterial(it) } ?: ThreeMaterials.DEFAULT } @@ -162,7 +160,7 @@ public fun Mesh.updateMaterialProperty(vision: Vision, propertyName: Name) { inherit = true, includeStyles = true, includeDefaults = false - )?.getColor() ?: ThreeMaterials.DEFAULT_COLOR + )?.threeColor() ?: ThreeMaterials.DEFAULT_COLOR material.needsUpdate = true } SolidMaterial.MATERIAL_OPACITY_KEY -> { diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreePlugin.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreePlugin.kt index e6d85dc5..9c7c1f6b 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreePlugin.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreePlugin.kt @@ -143,7 +143,7 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer { element, vision as? Solid ?: error("Solid expected but ${vision::class} found"), ).apply { - options.update(meta) + options.meta.update(meta) } } @@ -195,8 +195,4 @@ internal fun Object3D.findChild(name: Name): Object3D? { name.length == 1 -> this.children.find { it.name == name.tokens.first().toString() } else -> findChild(name.tokens.first().asName())?.findChild(name.cutFirst()) } -} - -public fun Context.withThreeJs(): Context = apply { - plugins.fetch(ThreePlugin) } \ No newline at end of file diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeReferenceFactory.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeReferenceFactory.kt index 33fd3ccb..4dca0eeb 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeReferenceFactory.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeReferenceFactory.kt @@ -3,9 +3,9 @@ package space.kscience.visionforge.solid.three import info.laht.threekt.core.BufferGeometry import info.laht.threekt.core.Object3D import info.laht.threekt.objects.Mesh +import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.cutFirst import space.kscience.dataforge.names.firstOrNull -import space.kscience.dataforge.names.toName import space.kscience.visionforge.onPropertyChange import space.kscience.visionforge.solid.Solid import space.kscience.visionforge.solid.SolidReferenceGroup @@ -49,7 +49,7 @@ public object ThreeReferenceFactory : ThreeFactory { obj.onPropertyChange(three.updateScope) { name-> if (name.firstOrNull()?.body == REFERENCE_CHILD_PROPERTY_PREFIX) { - val childName = name.firstOrNull()?.index?.toName() ?: error("Wrong syntax for reference child property: '$name'") + val childName = name.firstOrNull()?.index?.let(Name::parse) ?: error("Wrong syntax for reference child property: '$name'") val propertyName = name.cutFirst() val referenceChild = obj[childName] ?: error("Reference child with name '$childName' not found") val child = object3D.findChild(childName) ?: error("Object child with name '$childName' not found") diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/three.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/three.kt index 9e78800b..ea609e58 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/three.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/three.kt @@ -8,7 +8,7 @@ import info.laht.threekt.math.Euler import info.laht.threekt.math.Vector3 import info.laht.threekt.objects.Mesh import info.laht.threekt.textures.Texture -import space.kscience.dataforge.meta.MetaItem +import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.float import space.kscience.dataforge.meta.get import space.kscience.dataforge.meta.node @@ -17,7 +17,7 @@ import kotlin.math.PI public val Solid.euler: Euler get() = Euler(rotationX, rotationY, rotationZ, rotationOrder.name) -public val MetaItem.vector: Vector3 get() = Vector3(node["x"].float ?: 0f, node["y"].float ?: 0f, node["z"].float ?: 0f) +public val Meta.vector: Vector3 get() = Vector3(this["x"].float ?: 0f, this["y"].float ?: 0f, this["z"].float ?: 0f) internal fun Double.toRadians() = this * PI / 180 From 7b78052f617b6668dded0690e7540ea9c27c7e2c Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 8 Aug 2021 22:17:50 +0300 Subject: [PATCH 04/15] A lot of changes --- build.gradle.kts | 2 +- demo/gdml/build.gradle.kts | 5 +- .../visionforge/gdml/GDMLVisionTest.kt | 4 +- .../visionforge/gdml/demo/GdmlFxDemoApp.kt | 6 +- .../kscience/visionforge/solid/demo/demo.kt | 9 +- .../visionforge/solid/demo/VariableBox.kt | 9 +- ui/bootstrap/build.gradle.kts | 2 +- .../bootstrap/visionPropertyEditor.kt | 9 +- .../ThreeViewWithControls.kt | 7 +- .../ringPropertyEditor.kt | 9 +- .../visionforge/ComputedVisionProperties.kt | 53 ++++++ .../space/kscience/visionforge/StyleSheet.kt | 18 +- .../space/kscience/visionforge/Vision.kt | 122 ++++++------- .../space/kscience/visionforge/VisionBase.kt | 165 ++++++++++-------- .../kscience/visionforge/VisionChange.kt | 4 +- .../kscience/visionforge/VisionGroupBase.kt | 12 +- .../kscience/visionforge/VisionProperties.kt | 38 ---- .../visionforge/VisionPropertyContainer.kt | 31 +--- .../kotlin/space/kscience/visionforge/misc.kt | 11 +- .../kscience/visionforge/schemeDesctiptors.kt | 66 ------- .../kscience/visionforge/visionDelegates.kt | 76 ++++---- .../kscience/visionforge/visionDescriptor.kt | 9 - .../kscience/visionforge/html/HtmlTagTest.kt | 11 +- .../visionforge/meta/VisionPropertyTest.kt | 44 +++++ .../kscience/visionforge/VisionClient.kt | 2 +- .../visionforge/editor/FXMetaModel.kt | 154 ++-------------- .../visionforge/editor/MutableMetaEditor.kt | 54 +----- .../editor/VisionEditorFragment.kt | 45 ++--- .../kscience/visionforge/solid/FX3DPlugin.kt | 3 +- .../visionforge/solid/FXReferenceFactory.kt | 2 +- .../solid/VisualObjectFXBinding.kt | 5 +- .../visionforge/markup/VisionOfMarkup.kt | 2 +- .../visionforge/plotly/VisionOfPlotly.kt | 4 +- .../visionforge/three/server/VisionServer.kt | 2 +- .../visionforge/solid/ColorAccessor.kt | 13 +- .../kscience/visionforge/solid/Composite.kt | 15 +- .../kscience/visionforge/solid/Extruded.kt | 5 +- .../space/kscience/visionforge/solid/Solid.kt | 29 ++- .../kscience/visionforge/solid/SolidBase.kt | 4 +- .../kscience/visionforge/solid/SolidGroup.kt | 4 +- .../visionforge/solid/SolidMaterial.kt | 24 ++- .../visionforge/solid/SolidReference.kt | 81 +++------ .../visionforge/solid/specifications/Axes.kt | 2 +- .../solid/specifications/Camera.kt | 16 +- .../solid/specifications/Canvas3DOptions.kt | 27 +-- .../solid/transform/RemoveSingleChild.kt | 3 +- .../visionforge/solid/PropertyTest.kt | 19 +- .../visionforge/solid/SerializationTest.kt | 5 +- .../visionforge/solid/VisionUpdateTest.kt | 2 +- .../solid/three/MeshThreeFactory.kt | 15 +- .../solid/three/ThreeCompositeFactory.kt | 2 +- .../solid/three/ThreeGeometryBuilder.kt | 2 +- .../solid/three/ThreeLabelFactory.kt | 2 +- .../solid/three/ThreeLineFactory.kt | 5 +- .../visionforge/solid/three/ThreeMaterials.kt | 31 ++-- .../visionforge/solid/three/ThreePlugin.kt | 2 +- .../solid/three/ThreeReferenceFactory.kt | 2 +- .../kscience/visionforge/solid/three/csg.kt | 3 +- 58 files changed, 524 insertions(+), 784 deletions(-) create mode 100644 visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/ComputedVisionProperties.kt delete mode 100644 visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionProperties.kt delete mode 100644 visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/schemeDesctiptors.kt create mode 100644 visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/meta/VisionPropertyTest.kt diff --git a/build.gradle.kts b/build.gradle.kts index 22615a5d..ec691447 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,7 +2,7 @@ plugins { id("ru.mipt.npm.gradle.project") } -val dataforgeVersion by extra("0.5.0-dev-9") +val dataforgeVersion by extra("0.5.0-dev-10") val fxVersion by extra("11") allprojects { diff --git a/demo/gdml/build.gradle.kts b/demo/gdml/build.gradle.kts index 391db4f9..ead65970 100644 --- a/demo/gdml/build.gradle.kts +++ b/demo/gdml/build.gradle.kts @@ -16,7 +16,7 @@ kotlin { jvm { withJava() } - js{ + js { useCommonJs() browser { commonWebpackConfig { @@ -34,6 +34,7 @@ kotlin { jvmMain { dependencies { implementation(project(":visionforge-fx")) + implementation("ch.qos.logback:logback-classic:1.2.5") } } jsMain { @@ -53,5 +54,5 @@ application { val convertGdmlToJson by tasks.creating(JavaExec::class) { group = "application" classpath = sourceSets["main"].runtimeClasspath - main = "space.kscience.dataforge.vis.spatial.gdml.demo.SaveToJsonKt" + mainClass.set("space.kscience.dataforge.vis.spatial.gdml.demo.SaveToJsonKt") } \ No newline at end of file diff --git a/demo/gdml/src/commonTest/kotlin/space/kscience/visionforge/gdml/GDMLVisionTest.kt b/demo/gdml/src/commonTest/kotlin/space/kscience/visionforge/gdml/GDMLVisionTest.kt index 66332d9d..50ba6d12 100644 --- a/demo/gdml/src/commonTest/kotlin/space/kscience/visionforge/gdml/GDMLVisionTest.kt +++ b/demo/gdml/src/commonTest/kotlin/space/kscience/visionforge/gdml/GDMLVisionTest.kt @@ -1,8 +1,8 @@ package space.kscience.visionforge.gdml -import space.kscience.dataforge.meta.string import space.kscience.dataforge.names.Name import space.kscience.dataforge.values.asValue +import space.kscience.dataforge.values.string import space.kscience.gdml.GdmlShowCase import space.kscience.visionforge.setProperty import space.kscience.visionforge.solid.SolidMaterial @@ -27,6 +27,6 @@ class GDMLVisionTest { val child = vision[Name.of("composite-000","segment-0")] assertNotNull(child) child.setProperty(SolidMaterial.MATERIAL_COLOR_KEY, "red".asValue()) - assertEquals("red", child.getProperty(SolidMaterial.MATERIAL_COLOR_KEY).string) + assertEquals("red", child.getPropertyValue(SolidMaterial.MATERIAL_COLOR_KEY)?.string) } } \ No newline at end of file diff --git a/demo/gdml/src/jvmMain/kotlin/space/kscience/visionforge/gdml/demo/GdmlFxDemoApp.kt b/demo/gdml/src/jvmMain/kotlin/space/kscience/visionforge/gdml/demo/GdmlFxDemoApp.kt index d4cd85b0..cb40431c 100644 --- a/demo/gdml/src/jvmMain/kotlin/space/kscience/visionforge/gdml/demo/GdmlFxDemoApp.kt +++ b/demo/gdml/src/jvmMain/kotlin/space/kscience/visionforge/gdml/demo/GdmlFxDemoApp.kt @@ -7,7 +7,7 @@ import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.fetch import space.kscience.gdml.GdmlShowCase import space.kscience.visionforge.VisionManager -import space.kscience.visionforge.describedProperties +import space.kscience.visionforge.computeProperties import space.kscience.visionforge.editor.VisionEditorFragment import space.kscience.visionforge.editor.VisionTreeFragment import space.kscience.visionforge.gdml.toVision @@ -34,10 +34,10 @@ class GDMLView : View() { } private val propertyEditor = VisionEditorFragment { - it.describedProperties + it.computeProperties() }.apply { descriptorProperty.set(SolidMaterial.descriptor) - itemProperty.bind(treeFragment.selectedProperty) + visionProperty.bind(treeFragment.selectedProperty) } override val root: Parent = borderpane { diff --git a/demo/solid-showcase/src/commonMain/kotlin/space/kscience/visionforge/solid/demo/demo.kt b/demo/solid-showcase/src/commonMain/kotlin/space/kscience/visionforge/solid/demo/demo.kt index fcf94589..c933c3ce 100644 --- a/demo/solid-showcase/src/commonMain/kotlin/space/kscience/visionforge/solid/demo/demo.kt +++ b/demo/solid-showcase/src/commonMain/kotlin/space/kscience/visionforge/solid/demo/demo.kt @@ -1,9 +1,6 @@ package space.kscience.visionforge.solid.demo -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.delay -import kotlinx.coroutines.isActive -import kotlinx.coroutines.launch +import kotlinx.coroutines.* import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.invoke import space.kscience.dataforge.names.Name @@ -76,7 +73,7 @@ fun VisionLayout.showcase() { //override color for this cube color(1530) - launch(Dispatchers.Main) { + GlobalScope.launch(Dispatchers.Main) { while (isActive) { delay(500) visible = !(visible ?: false) @@ -85,7 +82,7 @@ fun VisionLayout.showcase() { } } - launch(Dispatchers.Main) { + GlobalScope.launch(Dispatchers.Main) { val random = Random(111) while (isActive) { delay(1000) diff --git a/demo/solid-showcase/src/jsMain/kotlin/space/kscience/visionforge/solid/demo/VariableBox.kt b/demo/solid-showcase/src/jsMain/kotlin/space/kscience/visionforge/solid/demo/VariableBox.kt index 22cac2c6..af828f46 100644 --- a/demo/solid-showcase/src/jsMain/kotlin/space/kscience/visionforge/solid/demo/VariableBox.kt +++ b/demo/solid-showcase/src/jsMain/kotlin/space/kscience/visionforge/solid/demo/VariableBox.kt @@ -3,6 +3,7 @@ package space.kscience.visionforge.solid.demo import info.laht.threekt.core.Object3D 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.number import space.kscience.dataforge.names.asName @@ -43,13 +44,13 @@ internal class VariableBox(val xSize: Number, val ySize: Number) : ThreeJsVision it.layers.enable(this@VariableBox.layer) } } - mesh.scale.z = getOwnProperty(VALUE).number?.toDouble() ?: 1.0 + mesh.scale.z = meta[VALUE].number?.toDouble() ?: 1.0 //add listener to object properties - onPropertyChange(three.context) { name -> + onPropertyChange { name -> when { name == VALUE -> { - val value = getOwnProperty(VALUE).int ?: 0 + val value = meta.get(VALUE).int ?: 0 val size = value.toFloat() / 255f * 20f mesh.scale.z = size.toDouble() mesh.position.z = size.toDouble() / 2 @@ -69,7 +70,7 @@ internal class VariableBox(val xSize: Number, val ySize: Number) : ThreeJsVision } var value: Int - get() = getOwnProperty(VALUE).int ?: 0 + get() = meta[VALUE].int ?: 0 set(value) { setProperty(VALUE, value.asValue()) } diff --git a/ui/bootstrap/build.gradle.kts b/ui/bootstrap/build.gradle.kts index af4ec727..b1b0588e 100644 --- a/ui/bootstrap/build.gradle.kts +++ b/ui/bootstrap/build.gradle.kts @@ -11,5 +11,5 @@ dependencies { implementation(npm("file-saver", "2.0.2")) implementation(npm("bootstrap","4.6.0")) implementation(npm("jquery","3.5.1")) - implementation(npm("@popperjs/core","2.9.3")) + implementation(npm("popper.js","1.16.1")) } \ No newline at end of file diff --git a/ui/bootstrap/src/main/kotlin/space/kscience/visionforge/bootstrap/visionPropertyEditor.kt b/ui/bootstrap/src/main/kotlin/space/kscience/visionforge/bootstrap/visionPropertyEditor.kt index 9c2206f1..65255af6 100644 --- a/ui/bootstrap/src/main/kotlin/space/kscience/visionforge/bootstrap/visionPropertyEditor.kt +++ b/ui/bootstrap/src/main/kotlin/space/kscience/visionforge/bootstrap/visionPropertyEditor.kt @@ -4,13 +4,10 @@ import org.w3c.dom.Element import react.RBuilder import react.dom.render import space.kscience.dataforge.meta.descriptors.MetaDescriptor -import space.kscience.visionforge.Vision -import space.kscience.visionforge.getStyle -import space.kscience.visionforge.meta +import space.kscience.visionforge.* import space.kscience.visionforge.react.metaViewer import space.kscience.visionforge.react.propertyEditor import space.kscience.visionforge.solid.SolidReference -import space.kscience.visionforge.styles public fun RBuilder.visionPropertyEditor( vision: Vision, @@ -20,8 +17,8 @@ public fun RBuilder.visionPropertyEditor( card("Properties") { propertyEditor( - ownProperties = vision.meta(false,false,false), - allProperties = vision.meta(), + ownProperties = vision.meta, + allProperties = vision.computeProperties(), updateFlow = vision.propertyChanges, descriptor = descriptor, key = key diff --git a/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ThreeViewWithControls.kt b/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ThreeViewWithControls.kt index d0aa2ed8..f8439a07 100644 --- a/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ThreeViewWithControls.kt +++ b/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ThreeViewWithControls.kt @@ -14,7 +14,8 @@ import space.kscience.dataforge.names.NameToken import space.kscience.dataforge.names.isEmpty import space.kscience.dataforge.names.length import space.kscience.visionforge.VisionGroup -import space.kscience.visionforge.meta +import space.kscience.visionforge.computeProperties +import space.kscience.visionforge.propertyChanges import space.kscience.visionforge.react.ThreeCanvasComponent import space.kscience.visionforge.react.flexColumn import space.kscience.visionforge.react.flexRow @@ -134,8 +135,8 @@ public val ThreeCanvasWithControls: FunctionComponent + get() { + val metaKeys = vision.meta.items.keys + val descriptorKeys = descriptor?.children?.map { NameToken(it.key) } ?: emptySet() + return (metaKeys + descriptorKeys).associateWith { getMeta(rootName + it) } + } + + override var value: Value? + get() { + val inheritFlag = descriptor?.inherited ?: false + val stylesFlag = descriptor?.usesStyles ?: true + return vision.getPropertyValue(rootName, inheritFlag, stylesFlag, true) + } + set(value) { + vision.meta.setValue(rootName, value) + } + + override fun getMeta(name: Name): ObservableMutableMeta = + ComputedVisionProperties(vision, rootName + name, visionDescriptor) + + override fun getOrCreate(name: Name): ObservableMutableMeta = getMeta(name) + + override fun toMeta(): Meta = this +} + +/** + * Compute property node based on inheritance and style information from the descriptor + */ +public fun Vision.computeProperties(descriptor: MetaDescriptor? = this.descriptor): ObservableMutableMeta = + if (descriptor == null) meta else ComputedVisionProperties(this, Name.EMPTY, descriptor) + +public fun Vision.computePropertyNode(name: Name, descriptor: MetaDescriptor? = this.descriptor): ObservableMutableMeta? = + computeProperties(descriptor)[name] \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/StyleSheet.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/StyleSheet.kt index e2869df8..dacc09b1 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/StyleSheet.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/StyleSheet.kt @@ -5,6 +5,7 @@ import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.NameToken import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.plus +import space.kscience.dataforge.values.Value import space.kscience.dataforge.values.asValue import kotlin.jvm.JvmInline @@ -14,7 +15,7 @@ import kotlin.jvm.JvmInline @JvmInline public value class StyleSheet(private val owner: VisionGroup) { - private val styleNode: Meta? get() = owner.getOwnProperty(STYLESHEET_KEY) + private val styleNode: Meta? get() = owner.meta[STYLESHEET_KEY] public val items: Map? get() = styleNode?.items @@ -24,7 +25,7 @@ public value class StyleSheet(private val owner: VisionGroup) { * Define a style without notifying owner */ public fun define(key: String, style: Meta?) { - owner.setPropertyNode(STYLESHEET_KEY + key, style) + owner.meta.setMeta(STYLESHEET_KEY + key, style) } /** @@ -71,9 +72,9 @@ internal fun Vision.styleChanged(key: String, oldStyle: Meta?, newStyle: Meta?) * List of names of styles applied to this object. Order matters. Not inherited. */ public var Vision.styles: List - get() = getOwnProperty(Vision.STYLE_KEY)?.stringList ?: emptyList() + get() = meta.getMeta(Vision.STYLE_KEY)?.stringList ?: emptyList() set(value) { - setPropertyValue(Vision.STYLE_KEY, value.map { it.asValue() }.asValue()) + meta.setValue(Vision.STYLE_KEY, value.map { it.asValue() }.asValue()) } /** @@ -86,7 +87,7 @@ public val VisionGroup.styleSheet: StyleSheet get() = StyleSheet(this) * Add style name to the list of styles to be resolved later. The style with given name does not necessary exist at the moment. */ public fun Vision.useStyle(name: String) { - styles = (getOwnProperty(Vision.STYLE_KEY)?.stringList ?: emptyList()) + name + styles = (meta.getMeta(Vision.STYLE_KEY)?.stringList ?: emptyList()) + name } @@ -94,7 +95,12 @@ public fun Vision.useStyle(name: String) { * Find a style with given name for given [Vision]. The style is not necessary applied to this [Vision]. */ public tailrec fun Vision.getStyle(name: String): Meta? = - getOwnProperty(StyleSheet.STYLESHEET_KEY + name) ?: parent?.getStyle(name) + meta.getMeta(StyleSheet.STYLESHEET_KEY + name) ?: parent?.getStyle(name) + +/** + * Resolve a property from all styles + */ +public fun Vision.getStyleProperty(name: Name): Value? = styles.firstNotNullOfOrNull { getStyle(it)?.get(name)?.value } /** * Resolve an item in all style layers diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/Vision.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/Vision.kt index daa854a5..e7bdd301 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/Vision.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/Vision.kt @@ -1,27 +1,27 @@ package space.kscience.visionforge -import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.meta.MutableMeta +import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.launch +import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.descriptors.Described import space.kscience.dataforge.meta.descriptors.MetaDescriptor -import space.kscience.dataforge.meta.update +import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.misc.Type import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.asName 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 kotlin.coroutines.CoroutineContext -import kotlin.coroutines.EmptyCoroutineContext /** * A root type for display hierarchy */ @Type(TYPE) -public interface Vision : Described, CoroutineScope { +public interface Vision : Described, Configurable { /** * The parent object of this one. If null, this one is a root. @@ -33,47 +33,23 @@ public interface Vision : Described, CoroutineScope { */ public val manager: VisionManager? get() = parent?.manager - override val coroutineContext: CoroutineContext - get() = manager?.context?.coroutineContext ?: EmptyCoroutineContext + /** + * This Vision own properties (ignoring inheritance, styles and defaults + */ + override val meta: ObservableMutableMeta /** - * Get property. + * 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. Null means inference from descriptor. Default is true. + * @param includeStyles toggles inclusion of properties from styles. default is true */ - public fun getProperty( + public fun getPropertyValue( name: Name, inherit: Boolean = false, includeStyles: Boolean = true, includeDefaults: Boolean = true, - ): Meta? + ): Value? - /** - * Get an intrinsic property of this Vision excluding any inheritance or defaults. In most cases should be the same as - * `getProperty(name, false, false, false`. - */ - public fun getOwnProperty(name: Name): Meta? = getProperty( - name, - inherit = false, - includeStyles = false, - includeDefaults = false - ) - - /** - * Replace the property node. If [node] is null remove node and its descendants - */ - public fun setPropertyNode(name: Name, node: Meta?, notify: Boolean = true) - - /** - * Set a value of specific property node - */ - public fun setPropertyValue(name: Name, value: Value?, notify: Boolean = true) - - /** - * Flow of property invalidation events. It does not contain property values after invalidation since it is not clear - * if it should include inherited properties etc. - */ - public val propertyChanges: Flow /** * Notify all listeners that a property has been changed and should be invalidated @@ -83,7 +59,7 @@ public interface Vision : Described, CoroutineScope { /** * Update this vision using a dif represented by [VisionChange]. */ - public fun change(change: VisionChange) + public fun update(change: VisionChange) override val descriptor: MetaDescriptor? @@ -96,58 +72,60 @@ public interface Vision : Described, CoroutineScope { } /** - * Subscribe on property updates. The subscription is bound to the given [scope] and canceled when the scope is canceled + * Flow of property invalidation events. It does not contain property values after invalidation since it is not clear + * if it should include inherited properties etc. */ -public fun Vision.onPropertyChange(scope: CoroutineScope, callback: suspend (Name) -> Unit) { - propertyChanges.onEach(callback).launchIn(scope) -} - +@OptIn(ExperimentalCoroutinesApi::class) +@DFExperimental +public val Vision.propertyChanges: Flow + get() = callbackFlow { + meta.onChange(this) { name -> + launch { + send(name) + } + } + awaitClose { + meta.removeListener(this) + } + } /** - * Own properties, excluding inheritance, styles and descriptor + * Subscribe on property updates. The subscription is bound to the given [scope] and canceled when the scope is canceled */ -public fun Vision.meta( - inherit: Boolean = false, - includeStyles: Boolean = true, - includeDefaults: Boolean = true, -): MutableMeta = VisionProperties(this, Name.EMPTY, inherit, includeStyles, includeDefaults) - -public fun Vision.configure(target: Name = Name.EMPTY, block: MutableMeta.() -> Unit): Unit { - VisionProperties(this, target).apply(block) +public fun Vision.onPropertyChange(callback: Meta.(Name) -> Unit) { + meta.onChange(null, callback) } -public fun Vision.configure(meta: Meta) { - configure(Name.EMPTY) { - update(meta) - } -} - -public fun Vision.configure(block: MutableMeta.() -> Unit): Unit = configure(Meta(block)) - -public fun Vision.getOwnProperty(key: String): Meta? = getOwnProperty(Name.parse(key)) - /** * Get [Vision] property using key as a String */ -public fun Vision.getProperty( +public fun Vision.getPropertyValue( key: String, inherit: Boolean = false, includeStyles: Boolean = true, includeDefaults: Boolean = true, -): Meta? = getProperty(Name.parse(key), inherit, includeStyles, includeDefaults) - +): 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 -> setPropertyNode(name, null) - is Meta -> setPropertyNode(name, item) - is Value -> setPropertyValue(name, 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()) + diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionBase.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionBase.kt index 46e5f62a..c123938d 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionBase.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionBase.kt @@ -1,24 +1,27 @@ package space.kscience.visionforge -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.launch import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.Transient -import space.kscience.dataforge.meta.* +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.defaultNode import space.kscience.dataforge.meta.descriptors.value +import space.kscience.dataforge.meta.get import space.kscience.dataforge.misc.DFExperimental -import space.kscience.dataforge.names.Name -import space.kscience.dataforge.names.asName -import space.kscience.dataforge.names.plus +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 properties Object own properties excluding styles and inheritance @@ -27,12 +30,9 @@ import kotlin.jvm.Synchronized @SerialName("vision") public open class VisionBase( @Transient override var parent: VisionGroup? = null, - @Serializable(MutableMetaSerializer::class) protected var properties: MutableMeta? = null ) : Vision { - //protected val observableProperties: ObservableMutableMeta by lazy { properties.asObservable() } - @Synchronized protected fun getOrCreateProperties(): MutableMeta { if (properties == null) { @@ -42,82 +42,105 @@ public open class VisionBase( return properties!! } - /** - * A fast accessor method to get own property (no inheritance or styles - */ - override fun getOwnProperty(name: Name): Meta? = properties?.getMeta(name) + @Transient + private val listeners = HashSet() - override fun getProperty( + private inner class VisionBaseProperties(val rootName: Name) : ObservableMutableMeta { + + override val items: Map + get() = properties?.get(rootName)?.items?.mapValues { entry -> + VisionBaseProperties(rootName + entry.key) + } ?: emptyMap() + + override var value: Value? + get() = properties?.get(rootName)?.value + set(value) { + getOrCreateProperties().setValue(rootName, value) + } + + override fun getOrCreate(name: Name): ObservableMutableMeta = VisionBaseProperties(this.rootName + name) + + override fun setMeta(name: Name, node: Meta?) { + getOrCreateProperties().setMeta(name, node) + } + + @DFExperimental + override fun attach(name: Name, node: ObservableMutableMeta) { + val ownProperties = getOrCreateProperties() + if (ownProperties is ObservableMutableMeta) { + ownProperties.attach(rootName + name, node) + } else { + ownProperties.setMeta(rootName + name, node) + node.onChange(this) { childName -> + ownProperties.setMeta(rootName + name + childName, this[childName]) + } + } + } + + override fun invalidate(name: Name) { + invalidateProperty(rootName + name) + } + + @Synchronized + override fun onChange(owner: Any?, callback: Meta.(name: Name) -> Unit) { + if (rootName.isEmpty()) { + listeners.add((MetaListener(owner, callback))) + } else { + listeners.add(MetaListener(owner) { name -> + if (name.startsWith(rootName)) { + (get(rootName) ?: Meta.EMPTY).callback(name.removeHeadOrNull(rootName)!!) + } + }) + } + } + + @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) + } + + override val meta: ObservableMutableMeta get() = VisionBaseProperties(Name.EMPTY) + + override fun getPropertyValue( name: Name, inherit: Boolean, includeStyles: Boolean, includeDefaults: Boolean, - ): Meta? = if (!inherit && !includeStyles && !includeDefaults) { - getOwnProperty(name) - } else { - buildList { - add(getOwnProperty(name)) - if (includeStyles) { - addAll(getStyleItems(name)) - } - if (inherit) { - add(parent?.getProperty(name, inherit, includeStyles, includeDefaults)) - } - if (includeDefaults) { - add(descriptor?.defaultNode?.get(name)) - } - }.merge() - } - - override fun setPropertyNode(name: Name, node: Meta?, notify: Boolean) { - val oldItem = properties?.get(name) - if (oldItem != node) { - getOrCreateProperties().setMeta(name, node) - if (notify) { - invalidateProperty(name) - } + ): Value? { + properties?.get(name)?.value?.let { return it } + if (includeStyles) { + getStyleProperty(name)?.let { return it } } - } - - override fun setPropertyValue(name: Name, value: Value?, notify: Boolean) { - val oldItem = properties?.get(name)?.value - if (oldItem != value) { - getOrCreateProperties()[name] = value - if (notify) { - invalidateProperty(name) - } + 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 - private suspend fun updateStyles(names: List) { - names.mapNotNull { getStyle(it) }.asSequence() - .flatMap { it.items.asSequence() } - .distinctBy { it.key } - .forEach { - invalidateProperty(it.key.asName()) - } - } - - //TODO check memory consumption for the flow - @Transient - private val propertyInvalidationFlow: MutableSharedFlow = MutableSharedFlow() - - @DFExperimental - override val propertyChanges: Flow - get() = propertyInvalidationFlow override fun invalidateProperty(propertyName: Name) { - launch { - if (propertyName == STYLE_KEY) { - updateStyles(styles) - } - propertyInvalidationFlow.emit(propertyName) + 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 change(change: VisionChange) { + override fun update(change: VisionChange) { change.properties?.let { updateProperties(Name.EMPTY, it) } @@ -131,7 +154,7 @@ public open class VisionBase( } public fun Vision.updateProperties(at: Name, item: Meta) { - setPropertyValue(at, item.value) + meta.setValue(at, item.value) item.items.forEach { (token, item) -> updateProperties(at + token, item) } diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionChange.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionChange.kt index e538a89c..0275934b 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionChange.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionChange.kt @@ -89,8 +89,8 @@ private fun CoroutineScope.collectChange( ) { //Collect properties change - source.onPropertyChange(this) { propertyName -> - val newItem = source.getOwnProperty(propertyName) + source.onPropertyChange { propertyName -> + val newItem = source.meta[propertyName] collector().propertyChanged(name, propertyName, newItem) } diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroupBase.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroupBase.kt index aab27bc5..f4d25b08 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroupBase.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroupBase.kt @@ -1,5 +1,6 @@ package space.kscience.visionforge +import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.launch @@ -8,7 +9,6 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.Transient import space.kscience.dataforge.names.* - /** * Abstract implementation of mutable group of [Vision] * @@ -48,7 +48,7 @@ public open class VisionGroupBase( * Propagate children change event upwards */ private fun childrenChanged(name: NameToken, before: Vision?, after: Vision?) { - launch { + (manager?.context?: GlobalScope).launch { _structureChanges.emit(MutableVisionGroup.StructureChange(name, before, after)) } } @@ -131,15 +131,15 @@ public open class VisionGroupBase( } } - override fun change(change: VisionChange) { + 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)?.change(change) + else -> get(name)?.update(change) } } - super.change(change) + super.update(change) } } @@ -151,6 +151,6 @@ internal class RootVisionGroup(override val manager: VisionManager) : VisionGrou /** * Designate this [VisionGroup] as a root group and assign a [VisionManager] as its parent */ -public fun Vision.root(manager: VisionManager){ +public fun Vision.root(manager: VisionManager) { parent = RootVisionGroup(manager) } \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionProperties.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionProperties.kt deleted file mode 100644 index 363ad729..00000000 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionProperties.kt +++ /dev/null @@ -1,38 +0,0 @@ -package space.kscience.visionforge - -import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.meta.MutableMeta -import space.kscience.dataforge.names.Name -import space.kscience.dataforge.names.NameToken -import space.kscience.dataforge.names.plus -import space.kscience.dataforge.values.Value - -internal class VisionProperties( - val vision: Vision, - val rootName: Name, - val inherit: Boolean = false, - val includeStyles: Boolean = true, - val includeDefaults: Boolean = true, -) : MutableMeta { - - override val items: Map - get() = vision.getProperty(rootName, inherit, includeStyles, includeDefaults)?.items?.mapValues { - VisionProperties(vision, rootName + it.key, inherit, includeStyles, includeDefaults) - } ?: emptyMap() - - override var value: Value? - get() = vision.getProperty(rootName, inherit, includeStyles, includeDefaults)?.value - set(value) { - vision.setPropertyValue(rootName, value) - } - - override fun getOrCreate(name: Name): MutableMeta = VisionProperties(vision, rootName + name) - - override fun setMeta(name: Name, node: Meta?) { - vision.setPropertyNode(rootName + name, node) - } - - override fun equals(other: Any?): Boolean = Meta.equals(this, other as? Meta) - override fun hashCode(): Int = Meta.hashCode(this) - override fun toString(): String = Meta.toString(this) -} \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionPropertyContainer.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionPropertyContainer.kt index f6f822b7..fed474fc 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionPropertyContainer.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionPropertyContainer.kt @@ -1,7 +1,7 @@ package space.kscience.visionforge import space.kscience.dataforge.meta.Configurable -import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.MutableMeta import space.kscience.dataforge.meta.ObservableMutableMeta import space.kscience.dataforge.meta.get import space.kscience.dataforge.names.Name @@ -11,39 +11,24 @@ import space.kscience.dataforge.values.Value * Property containers are used to create a symmetric behaviors for vision properties and style builders */ public interface VisionPropertyContainer { - public fun getProperty( + + public val meta: MutableMeta + + public fun getPropertyValue( name: Name, inherit: Boolean = false, includeStyles: Boolean = true, includeDefaults: Boolean = true, - ): Meta? - - /** - * Replace the property node. If [node] is null remove node and its descendants - */ - public fun setPropertyNode(name: Name, node: Meta?, notify: Boolean = true) - - /** - * Set a value of specific property node - */ - public fun setPropertyValue(name: Name, value: Value?, notify: Boolean = true) + ): Value? } public open class SimpleVisionPropertyContainer( override val meta: ObservableMutableMeta, ) : VisionPropertyContainer, Configurable { - override fun getProperty( + override fun getPropertyValue( name: Name, inherit: Boolean, includeStyles: Boolean, includeDefaults: Boolean - ): Meta? = meta[name] - - override fun setPropertyNode(name: Name, node: Meta?, notify: Boolean) { - this.meta.setMeta(name, node) - } - - override fun setPropertyValue(name: Name, value: Value?, notify: Boolean) { - this.meta.setValue(name, value) - } + ): Value? = meta[name]?.value } \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/misc.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/misc.kt index 900f6f1c..084416ac 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/misc.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/misc.kt @@ -2,9 +2,7 @@ package space.kscience.visionforge import space.kscience.dataforge.meta.Laminate import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.meta.boolean import space.kscience.dataforge.meta.isLeaf -import space.kscience.dataforge.values.asValue @DslMarker public annotation class VisionBuilder @@ -16,11 +14,4 @@ public fun List.merge(): Meta? { first.isLeaf -> first //fast search for first entry if it is value else -> Laminate(filterNotNull()) //merge nodes if first encountered node is meta } -} - -/** - * Control visibility of the element - */ -public var Vision.visible: Boolean? - get() = getProperty(Vision.VISIBLE_KEY).boolean - set(value) = setPropertyValue(Vision.VISIBLE_KEY, value?.asValue()) \ No newline at end of file +} \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/schemeDesctiptors.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/schemeDesctiptors.kt deleted file mode 100644 index 403312ea..00000000 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/schemeDesctiptors.kt +++ /dev/null @@ -1,66 +0,0 @@ -package space.kscience.visionforge - -import space.kscience.dataforge.meta.Scheme -import space.kscience.dataforge.meta.SchemeSpec -import space.kscience.dataforge.meta.descriptors.Described -import space.kscience.dataforge.meta.descriptors.MetaDescriptorBuilder -import space.kscience.dataforge.meta.descriptors.item -import space.kscience.dataforge.meta.descriptors.value -import space.kscience.dataforge.names.Name -import space.kscience.dataforge.values.ValueType -import kotlin.reflect.KProperty1 -import kotlin.reflect.typeOf - -/** - * TODO to be moved into the core - */ -public inline fun MetaDescriptorBuilder.value( - property: KProperty1, - noinline block: MetaDescriptorBuilder.() -> Unit = {}, -) { - when (typeOf()) { - typeOf(), typeOf(), typeOf(), typeOf(), typeOf(), typeOf() -> - value(property.name, ValueType.NUMBER) { - block() - } - typeOf(), typeOf(), typeOf(), typeOf(), typeOf(), typeOf() -> - value(property.name, ValueType.NUMBER) { - block() - } - typeOf() -> value(property.name, ValueType.BOOLEAN) { - block() - } - typeOf>(), typeOf>(), typeOf>(), typeOf>(), typeOf>(), typeOf>(), - typeOf(), typeOf(), typeOf(), typeOf(), typeOf(), - -> value(property.name, ValueType.NUMBER) { - multiple = true - block() - } - typeOf() -> value(property.name, ValueType.STRING) { - block() - } - typeOf>(), typeOf>() -> value(property.name, ValueType.STRING) { - multiple = true - block() - } - else -> item(property.name, block) - } -} - -public fun MetaDescriptorBuilder.item( - key: String, - described: Described, - block: MetaDescriptorBuilder.() -> Unit = {}, -) { - described.descriptor?.let { - item(Name.parse(key), it, block) - } -} - -public inline fun MetaDescriptorBuilder.scheme( - property: KProperty1, - spec: SchemeSpec, - noinline block: MetaDescriptorBuilder.() -> Unit = {}, -) { - item(property.name, spec, block) -} diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/visionDelegates.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/visionDelegates.kt index 524363d7..999bbb45 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/visionDelegates.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/visionDelegates.kt @@ -1,45 +1,43 @@ package space.kscience.visionforge -import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.meta.transformations.MetaConverter 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 = object : ReadWriteProperty { - 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?) { - setPropertyNode(name ?: Name.parse(property.name), value) - } -} - -public fun Vision.propertyNode( - converter: MetaConverter, - name: Name? = null, - inherit: Boolean = false, - includeStyles: Boolean = true, - includeDefaults: Boolean = true, -): ReadWriteProperty = object : ReadWriteProperty { - 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?) { - setPropertyNode(name ?: Name.parse(property.name), value?.let(converter::objectToMeta)) - } -} +//public fun Vision.propertyNode( +// name: Name? = null, +// inherit: Boolean = false, +// includeStyles: Boolean = true, +// includeDefaults: Boolean = true, +//): ReadWriteProperty = object : ReadWriteProperty { +// 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 Vision.propertyNode( +// converter: MetaConverter, +// name: Name? = null, +// inherit: Boolean = false, +// includeStyles: Boolean = true, +// includeDefaults: Boolean = true, +//): ReadWriteProperty = object : ReadWriteProperty { +// 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, @@ -48,10 +46,10 @@ public fun Vision.propertyValue( includeDefaults: Boolean = true, ): ReadWriteProperty = object : ReadWriteProperty { override fun getValue(thisRef: Any?, property: KProperty<*>): Value? = - getProperty(name ?: Name.parse(property.name), inherit, includeStyles, includeDefaults)?.value + getPropertyValue(name ?: Name.parse(property.name), inherit, includeStyles, includeDefaults) override fun setValue(thisRef: Any?, property: KProperty<*>, value: Value?) { - setPropertyValue(name ?: Name.parse(property.name), value) + meta.setValue(name ?: Name.parse(property.name), value) } } @@ -63,15 +61,15 @@ public fun Vision.propertyValue( setter: (T) -> Value? = { it?.let(Value::of) }, getter: (Value?) -> T, ): ReadWriteProperty = object : ReadWriteProperty { - override fun getValue(thisRef: Any?, property: KProperty<*>): T = getProperty( + override fun getValue(thisRef: Any?, property: KProperty<*>): T = getPropertyValue( name ?: Name.parse(property.name), inherit, includeStyles, includeDefaults - )?.value.let(getter) + ).let(getter) override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { - setPropertyValue(name ?: Name.parse(property.name), value?.let(setter)) + meta.setValue(name ?: Name.parse(property.name), value?.let(setter)) } } diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/visionDescriptor.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/visionDescriptor.kt index de505c50..a02a3b3e 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/visionDescriptor.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/visionDescriptor.kt @@ -2,7 +2,6 @@ package space.kscience.visionforge import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.descriptors.* -import space.kscience.dataforge.names.asName import space.kscience.dataforge.values.asValue private const val INHERITED_DESCRIPTOR_ATTRIBUTE = "inherited" @@ -23,14 +22,6 @@ public var MetaDescriptorBuilder.usesStyles: Boolean get() = attributes[STYLE_DESCRIPTOR_ATTRIBUTE].boolean ?: true set(value) = attributes.set(STYLE_DESCRIPTOR_ATTRIBUTE, value) - -public val Vision.describedProperties: Meta - get() = Meta { - descriptor?.children?.forEach { (key, descriptor) -> - this.setMeta(key.asName(), getProperty(key, inherit = descriptor.inherited)) - } - } - public val MetaDescriptor.widget: Meta get() = attributes["widget"] ?: Meta.EMPTY diff --git a/visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/html/HtmlTagTest.kt b/visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/html/HtmlTagTest.kt index 2a7c6d28..c09c7c6c 100644 --- a/visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/html/HtmlTagTest.kt +++ b/visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/html/HtmlTagTest.kt @@ -5,10 +5,17 @@ import kotlinx.html.stream.createHTML import space.kscience.dataforge.context.Global import space.kscience.dataforge.context.fetch import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.configure import space.kscience.dataforge.meta.set import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.names.Name -import space.kscience.visionforge.* +import space.kscience.visionforge.Vision +import space.kscience.visionforge.VisionBase +import space.kscience.visionforge.VisionManager +import kotlin.collections.HashMap +import kotlin.collections.Map +import kotlin.collections.forEach +import kotlin.collections.set import kotlin.test.Test typealias HtmlVisionRenderer = FlowContent.(name: Name, vision: Vision, meta: Meta) -> Unit @@ -57,7 +64,7 @@ class HtmlTagTest { div { h2 { +"Properties" } ul { - (vision as? VisionBase)?.meta()?.items?.forEach { + (vision as? VisionBase)?.meta?.items?.forEach { li { a { +it.key.toString() } p { +it.value.toString() } diff --git a/visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/meta/VisionPropertyTest.kt b/visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/meta/VisionPropertyTest.kt new file mode 100644 index 00000000..40b4c96e --- /dev/null +++ b/visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/meta/VisionPropertyTest.kt @@ -0,0 +1,44 @@ +package space.kscience.visionforge.meta + +import space.kscience.dataforge.meta.* +import space.kscience.dataforge.values.asValue +import space.kscience.visionforge.VisionBase +import kotlin.test.Test +import kotlin.test.assertEquals +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) + assertEquals(false, vision.meta["fff.ddd"]?.boolean) + } + + @Test + fun testPropertyEdit(){ + val vision = VisionBase() + vision.meta.getOrCreate("fff.ddd").apply { + value = 2.asValue() + } + assertEquals(2, vision.meta["fff.ddd"]?.int) + assertNotEquals(true, vision.meta["fff.ddd"]?.boolean) + } + + internal class TestScheme: Scheme(){ + var ddd by int() + companion object: SchemeSpec(::TestScheme) + } + + @Test + fun testPropertyUpdate(){ + val vision = VisionBase() + vision.meta.getOrCreate("fff").updateWith(TestScheme){ + ddd = 2 + } + assertEquals(2, vision.meta["fff.ddd"]?.int) + } +} \ No newline at end of file diff --git a/visionforge-core/src/jsMain/kotlin/space/kscience/visionforge/VisionClient.kt b/visionforge-core/src/jsMain/kotlin/space/kscience/visionforge/VisionClient.kt index ae691d4b..017c615d 100644 --- a/visionforge-core/src/jsMain/kotlin/space/kscience/visionforge/VisionClient.kt +++ b/visionforge-core/src/jsMain/kotlin/space/kscience/visionforge/VisionClient.kt @@ -92,7 +92,7 @@ public class VisionClient : AbstractPlugin() { } logger.debug { "Got update $change for output with name $name" } - vision.change(change) + vision.update(change) } else { console.error("WebSocket message data is not a string") } diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/FXMetaModel.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/FXMetaModel.kt index a3509221..476890a6 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/FXMetaModel.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/FXMetaModel.kt @@ -3,11 +3,14 @@ package space.kscience.visionforge.editor import javafx.beans.binding.Binding import javafx.beans.binding.BooleanBinding import javafx.beans.binding.ListBinding -import javafx.beans.property.SimpleObjectProperty +import javafx.beans.binding.ObjectBinding import javafx.collections.ObservableList -import space.kscience.dataforge.meta.* +import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.ObservableMeta +import space.kscience.dataforge.meta.boolean 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.* import space.kscience.dataforge.values.Value import tornadofx.* @@ -22,11 +25,13 @@ public class FXMetaModel( public val title: String = nodeName.lastOrNull()?.toString() ?: "Meta" ) : Comparable> { - private val existingNode = SimpleObjectProperty(root[nodeName]) + private val existingNode = object: ObjectBinding() { + override fun computeValue(): Meta? =root[nodeName] + } public val children: ListBinding> = object : ListBinding>() { override fun computeValue(): ObservableList> { - val nodeKeys = existingNode.get().items.keys + val nodeKeys = existingNode.get()?.items?.keys?: emptySet() val descriptorKeys = descriptor?.children?.keys?.map { NameToken(it) } ?: emptySet() return (nodeKeys + descriptorKeys).map { FXMetaModel( @@ -43,7 +48,7 @@ public class FXMetaModel( if (root is ObservableMeta) { root.onChange(this) { changed -> if (changed.startsWith(nodeName)) { - if (nodeName.length == changed.length) existingNode.set(root[nodeName]) + if (nodeName.length == changed.length) existingNode.invalidate() else if (changed.length == nodeName.length + 1) children.invalidate() } } @@ -57,7 +62,7 @@ public class FXMetaModel( public val exists: Boolean by existsProperty public val valueProperty: Binding = existingNode.objectBinding { - existingNode.get().value ?: descriptor?.defaultValue + existingNode.get()?.value ?: descriptor?.defaultValue } override fun compareTo(other: FXMetaModel<*>): Int = if (this.exists == other.exists) { @@ -77,141 +82,4 @@ public class FXMetaModel( rootName: String = "root" ): FXMetaModel = FXMetaModel(node, descriptor, Name.EMPTY, title = rootName) } - -// /** -// * A descriptor that could be manually set to the node -// */ -// private val innerDescriptorProperty = SimpleObjectProperty(descriptorValue) -// -// /** -// * Actual descriptor which holds value inferred from parrent -// */ -// val descriptorProperty: ObjectBinding = objectBinding(innerDescriptorProperty) { -// value ?: parent?.descriptor?.get(this@FXMeta.name.body) -// } -// -// val descriptor: MetaDescriptor? by descriptorProperty -// -// private val innerNodeProperty = SimpleObjectProperty(nodeValue) -// -// val nodeProperty: ObjectBinding = objectBinding(innerNodeProperty) { -// value ?: parent?.node?.get(this@FXMeta.name) -// } -// -// val node by nodeProperty -// -// val hasValue: ObservableBooleanValue = nodeProperty.booleanBinding { it != null } -// -// private val filter: (FXMeta) -> Boolean = { cfg -> -// !(cfg.descriptor?.attributes?.get(MutableMetaEditor.NO_CONFIGURATOR_TAG)?.boolean ?: false) -// } -// -// val children: ListBinding> = object : ListBinding>() { -// -// init { -// bind(nodeProperty, descriptorProperty) -// -// val listener: Meta.(Name) -> Unit = { name -> -// if (name.length == 1) invalidate() -// } -// -// (node as? ObservableMeta)?.onChange(this, listener) -// -// nodeProperty.addListener { _, oldValue, newValue -> -// if (newValue == null) { -// (oldValue as? ObservableMeta)?.removeListener(this) -// } -// -// if (newValue is ObservableMeta) { -// newValue.onChange(this, listener) -// } -// } -// } -// -// override fun computeValue(): ObservableList> { -// val nodeKeys = node?.items?.keys?.toSet() ?: emptySet() -// val descriptorKeys = descriptor?.children?.keys?.map { NameToken(it) } ?: emptyList() -// val keys: Set = nodeKeys + descriptorKeys -// -// val items = keys.map { token -> -// val actualItem = node?.items?.get(token) -// val actualDescriptor = descriptor?.children?.get(token.body) -// -// if (actualItem is MetaNode) { -// FXMetaNode(token, this@FXMetaNode) -// } else { -// FXMetaValue(token, this@FXMetaNode) -// } -// } -// -// return items.filter(filter).asObservable() -// } -// } -// -// init { -// if (parent != null) { -// parent.descriptorProperty.onChange { descriptorProperty.invalidate() } -// parent.nodeProperty.onChange { nodeProperty.invalidate() } -// } -// } -// -} - -// -//internal fun FXMeta.remove(name: NameToken) { -// node?.remove(name.asName()) -// children.invalidate() -//} -// -//private fun M.createEmptyNode(token: NameToken, append: Boolean): M { -// return if (append && token.hasIndex()) { -// val name = token.asName() -// val index = (getIndexed(name).keys.mapNotNull { it?.toIntOrNull() }.maxOrNull() ?: -1) + 1 -// val newName = name.withIndex(index.toString()) -// set(newName, Meta.EMPTY) -// get(newName).node -// } else { -// this.set(token.asName(), Meta.EMPTY) -// //FIXME possible concurrency bug -// get(token).node -// } -//} -// -//internal fun FXMeta.getOrCreateNode(): M { -// val node = node -// return when { -// node != null -> node -// parent != null -> parent.getOrCreateNode().createEmptyNode(this.name, descriptor?.multiple == true).also { -// parent.children.invalidate() -// } -// else -> kotlin.error("Orphan empty node is not allowed") -// } -// -//} - -internal fun FXMetaModel.remove() { - root.remove(nodeName) -} - -// -//internal fun FXMeta.addValue(key: String) { -// val parent = getOrCreateNode() -// if (descriptor?.multiple == true) { -// parent.append(key, Null) -// } else { -// parent[key] = Null -// } -//} -// -//internal fun FXMeta.addNode(key: String) { -// val parent = getOrCreateNode() -// if (descriptor?.multiple == true) { -// parent.append(key, Meta.EMPTY) -// } else { -// parent[key] = Meta.EMPTY -// } -//} -// -internal fun FXMetaModel.setValue(value: Value?) { - root.setValue(nodeName, value) } \ No newline at end of file diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/MutableMetaEditor.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/MutableMetaEditor.kt index c7238b44..59f49c38 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/MutableMetaEditor.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/MutableMetaEditor.kt @@ -5,6 +5,7 @@ */ package space.kscience.visionforge.editor +import javafx.beans.property.SimpleStringProperty import javafx.scene.control.* import javafx.scene.control.cell.TextFieldTreeTableCell import javafx.scene.layout.BorderPane @@ -13,6 +14,7 @@ import javafx.scene.text.Text import space.kscience.dataforge.context.Global import space.kscience.dataforge.meta.MutableMeta import space.kscience.dataforge.meta.descriptors.MetaDescriptor +import space.kscience.dataforge.meta.remove import space.kscience.visionforge.dfIconView import tornadofx.* @@ -32,8 +34,7 @@ public class MutableMetaEditor( MutableMeta: MutableMeta, descriptor: MetaDescriptor?, title: String = "Configuration editor" - ) : - this(FXMetaModel.root(MutableMeta, descriptor = descriptor), title = title) + ) : this(FXMetaModel.root(MutableMeta, descriptor = descriptor), title = title) override val root: BorderPane = borderpane { center = treetableview> { @@ -64,7 +65,7 @@ public class MutableMetaEditor( contextmenu { item("Remove") { action { - content.remove() + content.root.remove(content.nodeName) } } } @@ -83,7 +84,7 @@ public class MutableMetaEditor( } column("Description") { param: TreeTableColumn.CellDataFeatures, String> -> - (param.value.value.descriptor?.info ?: "").observable() + SimpleStringProperty(param.value.value.descriptor?.info ?: "") }.setCellFactory { param: TreeTableColumn, String> -> val cell = TreeTableCell, String>() val text = Text() @@ -126,51 +127,10 @@ public class MutableMetaEditor( Global, item.valueProperty, item.descriptor - ) { - item.setValue(it) + ) { value -> + item.root.setValue(item.nodeName, value) } graphic = chooser.node -// when (item) { -// is FXMetaValue -> { -// text = null -// val chooser = ValueChooser.build( -// Global, -// item.valueProperty, -// item.descriptor -// ) { -// item.set(it) -// } -// graphic = chooser.node -// } -// is FXMetaNode -> { -// if (allowNew) { -// text = null -// graphic = HBox().apply { -// val glyph: Node = FontAwesomeIconView(FontAwesomeIcon.PLUS_CIRCLE) -// button("node", graphic = glyph) { -// hgrow = Priority.ALWAYS -// maxWidth = Double.POSITIVE_INFINITY -// action { -// showNodeDialog()?.let { -// item.addNode(it) -// } -// } -// } -// button("value", graphic = FontAwesomeIconView(FontAwesomeIcon.PLUS_SQUARE)) { -// hgrow = Priority.ALWAYS -// maxWidth = Double.POSITIVE_INFINITY -// action { -// showValueDialog()?.let { -// item.addValue(it) -// } -// } -// } -// } -// } else { -// text = "" -// } -// } -// } } else { text = null diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/VisionEditorFragment.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/VisionEditorFragment.kt index 3ce3b2de..d159dd11 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/VisionEditorFragment.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/VisionEditorFragment.kt @@ -5,41 +5,22 @@ import javafx.beans.property.SimpleObjectProperty import javafx.scene.Node import javafx.scene.Parent import javafx.scene.layout.VBox -import space.kscience.dataforge.meta.* +import space.kscience.dataforge.meta.ObservableMutableMeta import space.kscience.dataforge.meta.descriptors.MetaDescriptor -import space.kscience.visionforge.* +import space.kscience.visionforge.Vision +import space.kscience.visionforge.computeProperties +import space.kscience.visionforge.getStyle +import space.kscience.visionforge.styles import tornadofx.* -public class VisionEditorFragment(public val selector: (Vision) -> Meta) : Fragment() { +public class VisionEditorFragment(public val selector: (Vision) -> ObservableMutableMeta = {it.computeProperties()}) : Fragment() { - public val itemProperty: SimpleObjectProperty = SimpleObjectProperty() - public var item: Vision? by itemProperty + public val visionProperty: SimpleObjectProperty = SimpleObjectProperty() + public var vision: Vision? by visionProperty public val descriptorProperty: SimpleObjectProperty = SimpleObjectProperty() - public constructor( - item: Vision?, - descriptor: MetaDescriptor?, - selector: (Vision) -> MutableMetaProvider = { it.meta() }, - ) : this({ it.describedProperties }) { - this.item = item - this.descriptorProperty.set(descriptor) - } - - private var currentConfig: ObservableMutableMeta? = null - - private val configProperty: Binding = itemProperty.objectBinding { vision -> - if (vision == null) return@objectBinding null - val meta = selector(vision) - val config = MutableMeta { - update(meta) - } - config.onChange(this@VisionEditorFragment) { key -> - vision.setPropertyNode(key, config[key]) - } - //remember old config reference to cleanup listeners - currentConfig?.removeListener(this) - currentConfig = config - config + private val configProperty: Binding = visionProperty.objectBinding { vision -> + vision?.let(selector) } private val configEditorProperty: Binding = configProperty.objectBinding(descriptorProperty) { @@ -50,8 +31,8 @@ public class VisionEditorFragment(public val selector: (Vision) -> Meta) : Fragm private val styleBoxProperty: Binding = configProperty.objectBinding() { VBox().apply { - item?.styles?.forEach { styleName -> - val styleMeta = item?.getStyle(styleName) + vision?.styles?.forEach { styleName -> + val styleMeta = vision?.getStyle(styleName) if (styleMeta != null) { titledpane(styleName, node = MetaViewer(styleMeta).root) } @@ -64,7 +45,7 @@ public class VisionEditorFragment(public val selector: (Vision) -> Meta) : Fragm contentProperty().bind(configEditorProperty) } titledpane("Styles", collapsible = false) { - visibleWhen(itemProperty.booleanBinding { it?.styles?.isNotEmpty() ?: false }) + visibleWhen(visionProperty.booleanBinding { it?.styles?.isNotEmpty() ?: false }) contentProperty().bind(styleBoxProperty) } } diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FX3DPlugin.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FX3DPlugin.kt index f843e02c..72a7ac8a 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FX3DPlugin.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FX3DPlugin.kt @@ -16,6 +16,7 @@ import space.kscience.dataforge.context.* import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.boolean import space.kscience.dataforge.misc.Type +import space.kscience.visionforge.computePropertyNode import space.kscience.visionforge.solid.FX3DFactory.Companion.TYPE import space.kscience.visionforge.solid.SolidMaterial.Companion.MATERIAL_KEY import space.kscience.visionforge.solid.SolidMaterial.Companion.MATERIAL_WIREFRAME_KEY @@ -73,7 +74,7 @@ class FX3DPlugin : AbstractPlugin() { is PolyLine -> PolyLine3D( obj.points.map { Point3D(it.x, it.y, it.z) }, obj.thickness.toFloat(), - obj.getProperty(SolidMaterial.MATERIAL_COLOR_KEY, inherit = true)?.color() + obj.computePropertyNode(SolidMaterial.MATERIAL_COLOR_KEY)?.color() ).apply { this.meshView.cullFace = CullFace.FRONT } diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXReferenceFactory.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXReferenceFactory.kt index d2863cea..ac05e8d5 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXReferenceFactory.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXReferenceFactory.kt @@ -17,7 +17,7 @@ class FXReferenceFactory(val plugin: FX3DPlugin) : FX3DFactory + obj.onPropertyChange { name-> if (name.firstOrNull()?.body == SolidReferenceGroup.REFERENCE_CHILD_PROPERTY_PREFIX) { val childName = name.firstOrNull()?.index?.let(Name::parse) ?: error("Wrong syntax for reference child property: '$name'") val propertyName = name.cutFirst() diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/VisualObjectFXBinding.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/VisualObjectFXBinding.kt index 492356ea..607913d7 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/VisualObjectFXBinding.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/VisualObjectFXBinding.kt @@ -7,6 +7,7 @@ import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.startsWith import space.kscience.dataforge.values.Value import space.kscience.visionforge.Vision +import space.kscience.visionforge.computePropertyNode import space.kscience.visionforge.onPropertyChange import tornadofx.* @@ -17,7 +18,7 @@ public class VisualObjectFXBinding(public val fx: FX3DPlugin, public val obj: Vi private val bindings = HashMap>() init { - obj.onPropertyChange(fx.context) { name -> + obj.onPropertyChange { name -> bindings.filter { it.key.startsWith(name) }.forEach { entry -> Platform.runLater { entry.value.invalidate() @@ -35,7 +36,7 @@ public class VisualObjectFXBinding(public val fx: FX3DPlugin, public val obj: Vi public operator fun get(key: Name): ObjectBinding { return bindings.getOrPut(key) { object : ObjectBinding() { - override fun computeValue(): Meta? = obj.getProperty(key) + override fun computeValue(): Meta? = obj.computePropertyNode(key) } } } diff --git a/visionforge-markdown/src/commonMain/kotlin/space/kscience/visionforge/markup/VisionOfMarkup.kt b/visionforge-markdown/src/commonMain/kotlin/space/kscience/visionforge/markup/VisionOfMarkup.kt index 2103078d..fd1dcd80 100644 --- a/visionforge-markdown/src/commonMain/kotlin/space/kscience/visionforge/markup/VisionOfMarkup.kt +++ b/visionforge-markdown/src/commonMain/kotlin/space/kscience/visionforge/markup/VisionOfMarkup.kt @@ -21,7 +21,7 @@ public class VisionOfMarkup( //TODO add templates public var content: String? - get() = getOwnProperty(CONTENT_PROPERTY_KEY).string + get() = meta.getMeta(CONTENT_PROPERTY_KEY).string set(value) { setProperty(CONTENT_PROPERTY_KEY, value) } diff --git a/visionforge-plotly/src/commonMain/kotlin/space/kscience/visionforge/plotly/VisionOfPlotly.kt b/visionforge-plotly/src/commonMain/kotlin/space/kscience/visionforge/plotly/VisionOfPlotly.kt index 7b2f1c25..36102036 100644 --- a/visionforge-plotly/src/commonMain/kotlin/space/kscience/visionforge/plotly/VisionOfPlotly.kt +++ b/visionforge-plotly/src/commonMain/kotlin/space/kscience/visionforge/plotly/VisionOfPlotly.kt @@ -2,8 +2,6 @@ package space.kscience.visionforge.plotly import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import space.kscience.dataforge.meta.MutableMeta -import space.kscience.dataforge.meta.asObservable import space.kscience.dataforge.misc.DFExperimental import space.kscience.plotly.Plot import space.kscience.plotly.Plotly @@ -18,7 +16,7 @@ public class VisionOfPlotly private constructor() : VisionBase() { properties = plot.meta } - public val plot: Plot get() = Plot(properties?.asObservable() ?: MutableMeta()) + public val plot: Plot get() = Plot(meta) } public fun Plot.asVision(): VisionOfPlotly = VisionOfPlotly(this) diff --git a/visionforge-server/src/main/kotlin/space/kscience/visionforge/three/server/VisionServer.kt b/visionforge-server/src/main/kotlin/space/kscience/visionforge/three/server/VisionServer.kt index 0a9c60e3..46cb2e66 100644 --- a/visionforge-server/src/main/kotlin/space/kscience/visionforge/three/server/VisionServer.kt +++ b/visionforge-server/src/main/kotlin/space/kscience/visionforge/three/server/VisionServer.kt @@ -137,7 +137,7 @@ public class VisionServer internal constructor( val change = visionManager.jsonFormat.decodeFromString( VisionChange.serializer(), it.data.decodeToString() ) - vision.change(change) + vision.update(change) } } diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/ColorAccessor.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/ColorAccessor.kt index 66efe69b..25631c42 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/ColorAccessor.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/ColorAccessor.kt @@ -6,20 +6,23 @@ import space.kscience.dataforge.values.Value import space.kscience.dataforge.values.asValue import space.kscience.dataforge.values.string import space.kscience.visionforge.Colors +import space.kscience.visionforge.Vision import space.kscience.visionforge.VisionBuilder +import space.kscience.visionforge.VisionPropertyContainer +import kotlin.jvm.JvmInline @VisionBuilder -public class ColorAccessor(private val parent: MutableMetaProvider, private val colorKey: Name) { +public class ColorAccessor(private val colorKey: Name, private val parent: () -> MutableMetaProvider) { public var value: Value? - get() = parent.getMeta(colorKey)?.value + get() = parent().getMeta(colorKey)?.value set(value) { - parent.setValue(colorKey,value) + parent().setValue(colorKey,value) } public var item: Meta? - get() = parent.getMeta(colorKey) + get() = parent().getMeta(colorKey) set(value) { - parent.setMeta(colorKey,value) + parent().setMeta(colorKey,value) } } diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Composite.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Composite.kt index 4017b11b..b3770d37 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Composite.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Composite.kt @@ -2,6 +2,7 @@ package space.kscience.visionforge.solid import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +import space.kscience.dataforge.meta.configure import space.kscience.dataforge.meta.update import space.kscience.visionforge.* @@ -29,20 +30,20 @@ public inline fun VisionContainerBuilder.composite( val group = SolidGroup().apply(builder) val children = group.children.values.filterIsInstance() if (children.size != 2) error("Composite requires exactly two children") - return Composite(type, children[0], children[1]).also { composite -> - composite.configure { - update(group.meta()) + return Composite(type, children[0], children[1]).apply { + configure { + update(group.meta) } if (group.position != null) { - composite.position = group.position + position = group.position } if (group.rotation != null) { - composite.rotation = group.rotation + rotation = group.rotation } if (group.scale != null) { - composite.scale = group.scale + scale = group.scale } - set(name, composite) + set(name, this) } } diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Extruded.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Extruded.kt index 0ecc02b0..b9e32bee 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Extruded.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Extruded.kt @@ -4,6 +4,7 @@ import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import space.kscience.dataforge.meta.MutableMeta import space.kscience.dataforge.meta.ObservableMutableMeta +import space.kscience.dataforge.meta.configure import space.kscience.visionforge.* import kotlin.math.PI import kotlin.math.cos @@ -106,7 +107,9 @@ public class ExtrudeBuilder( layers.add(Layer(x.toFloat(), y.toFloat(), z.toFloat(), scale.toFloat())) } - internal fun build(): Extruded = Extruded(shape, layers).apply { configure(meta()) } + internal fun build(): Extruded = Extruded(shape, layers).apply { + configure(this@ExtrudeBuilder.meta) + } } @VisionBuilder diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Solid.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Solid.kt index f2ad38fd..39ed06ac 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Solid.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Solid.kt @@ -5,8 +5,7 @@ import space.kscience.dataforge.meta.descriptors.* import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.plus -import space.kscience.dataforge.values.ValueType -import space.kscience.dataforge.values.asValue +import space.kscience.dataforge.values.* import space.kscience.visionforge.* import space.kscience.visionforge.Vision.Companion.VISIBLE_KEY import space.kscience.visionforge.solid.Solid.Companion.DETAIL_KEY @@ -76,6 +75,8 @@ public interface Solid : Vision { default(true) } + node(SolidMaterial.MATERIAL_KEY.toString(), SolidMaterial) + //TODO replace by descriptor merge value(Vision.STYLE_KEY, ValueType.STRING) { multiple = true @@ -98,10 +99,6 @@ public interface Solid : Vision { hide() } - item(SolidMaterial.MATERIAL_KEY.toString(), SolidMaterial){ - valueRequirement = ValueRequirement.ABSENT - } - enum(ROTATION_ORDER_KEY, default = RotationOrder.XYZ) { hide() } @@ -114,7 +111,7 @@ public interface Solid : Vision { * Get the layer number this solid belongs to. Return 0 if layer is not defined. */ public var Solid.layer: Int - get() = getProperty(LAYER_KEY, inherit = true).int ?: 0 + get() = getPropertyValue(LAYER_KEY, inherit = true)?.int ?: 0 set(value) { setProperty(LAYER_KEY, value) } @@ -134,24 +131,24 @@ public enum class RotationOrder { * Rotation order */ public var Solid.rotationOrder: RotationOrder - get() = getProperty(Solid.ROTATION_ORDER_KEY).enum() ?: RotationOrder.XYZ - set(value) = setPropertyValue(Solid.ROTATION_ORDER_KEY, value.name.asValue()) + get() = getPropertyValue(Solid.ROTATION_ORDER_KEY)?.enum() ?: RotationOrder.XYZ + set(value) = meta.setValue(Solid.ROTATION_ORDER_KEY, value.name.asValue()) /** * Preferred number of polygons for displaying the object. If not defined, uses shape or renderer default. Not inherited */ public var Solid.detail: Int? - get() = getProperty(DETAIL_KEY, false).int - set(value) = setPropertyValue(DETAIL_KEY, value?.asValue()) + get() = getPropertyValue(DETAIL_KEY, false)?.int + set(value) = meta.setValue(DETAIL_KEY, value?.asValue()) /** * If this property is true, the object will be ignored on render. * Property is not inherited. */ public var Vision.ignore: Boolean? - get() = getProperty(IGNORE_KEY, false).boolean - set(value) = setPropertyValue(IGNORE_KEY, value?.asValue()) + get() = getPropertyValue(IGNORE_KEY, false)?.boolean + set(value) = meta.setValue(IGNORE_KEY, value?.asValue()) //var VisualObject.selected: Boolean? // get() = getProperty(SELECTED_KEY).boolean @@ -160,7 +157,7 @@ public var Vision.ignore: Boolean? internal fun float(name: Name, default: Number): ReadWriteProperty = object : ReadWriteProperty { override fun getValue(thisRef: Solid, property: KProperty<*>): Number { - return thisRef.getOwnProperty(name)?.number ?: default + return thisRef.meta.getMeta(name)?.number ?: default } override fun setValue(thisRef: Solid, property: KProperty<*>, value: Number) { @@ -171,7 +168,7 @@ internal fun float(name: Name, default: Number): ReadWriteProperty = object : ReadWriteProperty { override fun getValue(thisRef: Solid, property: KProperty<*>): Point3D? { - val item = thisRef.getOwnProperty(name) ?: return null + val item = thisRef.meta.getMeta(name) ?: return null return object : Point3D { override val x: Float get() = item[X_KEY]?.float ?: default override val y: Float get() = item[Y_KEY]?.float ?: default @@ -181,7 +178,7 @@ internal fun point(name: Name, default: Float): ReadWriteProperty, value: Point3D?) { if (value == null) { - thisRef.setPropertyNode(name, null) + thisRef.meta.setMeta(name, null) } else { thisRef.setProperty(name + X_KEY, value.x) thisRef.setProperty(name + Y_KEY, value.y) diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidBase.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidBase.kt index 2beee1ed..f18a3869 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidBase.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidBase.kt @@ -11,8 +11,8 @@ import space.kscience.visionforge.VisionChange public open class SolidBase : VisionBase(), Solid { override val descriptor: MetaDescriptor get() = Solid.descriptor - override fun change(change: VisionChange) { + override fun update(change: VisionChange) { updatePosition(change.properties) - super.change(change) + super.update(change) } } diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidGroup.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidGroup.kt index bc6676f6..f0f28fc5 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidGroup.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidGroup.kt @@ -60,9 +60,9 @@ public class SolidGroup : VisionGroupBase(), Solid, PrototypeHolder { override fun createGroup(): SolidGroup = SolidGroup() - override fun change(change: VisionChange) { + override fun update(change: VisionChange) { updatePosition(change.properties) - super.change(change) + super.update(change) } public companion object { diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidMaterial.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidMaterial.kt index 3d9843a3..78d787a3 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidMaterial.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidMaterial.kt @@ -8,6 +8,7 @@ import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.plus import space.kscience.dataforge.values.ValueType import space.kscience.dataforge.values.asValue +import space.kscience.dataforge.values.number import space.kscience.visionforge.* import space.kscience.visionforge.solid.SolidMaterial.Companion.MATERIAL_COLOR_KEY import space.kscience.visionforge.solid.SolidMaterial.Companion.MATERIAL_KEY @@ -19,14 +20,14 @@ public class SolidMaterial : Scheme() { /** * Primary web-color for the material */ - public val color: ColorAccessor = ColorAccessor(this, COLOR_KEY) + public val color: ColorAccessor = ColorAccessor(COLOR_KEY) { meta } /** * Specular color for phong material */ - public val specularColor: ColorAccessor = ColorAccessor(this, SPECULAR_COLOR_KEY) + public val specularColor: ColorAccessor = ColorAccessor(SPECULAR_COLOR_KEY) { meta } - public val emissiveColor: ColorAccessor = ColorAccessor(this, "emissiveColor".asName()) + public val emissiveColor: ColorAccessor = ColorAccessor("emissiveColor".asName()) { meta } /** * Opacity @@ -89,24 +90,19 @@ public class SolidMaterial : Scheme() { } public val Solid.color: ColorAccessor - get() = ColorAccessor( - meta(inherit = true), - MATERIAL_COLOR_KEY - ) + get() = ColorAccessor(MATERIAL_COLOR_KEY) { computeProperties() } public var Solid.material: SolidMaterial? - get() = getProperty(MATERIAL_KEY, inherit = true)?.let { SolidMaterial.read(it) } - set(value) = setPropertyNode(MATERIAL_KEY, value?.meta) + get() = computePropertyNode(MATERIAL_KEY)?.let { SolidMaterial.read(it) } + set(value) = meta.setMeta(MATERIAL_KEY, value?.meta) @VisionBuilder public fun Solid.material(builder: SolidMaterial.() -> Unit) { - configure(MATERIAL_KEY){ - updateWith(SolidMaterial,builder) - } + meta.getOrCreate(MATERIAL_KEY).updateWith(SolidMaterial, builder) } public var Solid.opacity: Number? - get() = getProperty(MATERIAL_OPACITY_KEY, inherit = true).number + get() = getPropertyValue(MATERIAL_OPACITY_KEY, inherit = true)?.number set(value) { - setPropertyValue(MATERIAL_OPACITY_KEY, value?.asValue()) + meta.setValue(MATERIAL_OPACITY_KEY, value?.asValue()) } \ No newline at end of file diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidReference.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidReference.kt index 0149fc88..427ef9a2 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidReference.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidReference.kt @@ -1,12 +1,10 @@ package space.kscience.visionforge.solid -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.mapNotNull import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.ObservableMutableMeta import space.kscience.dataforge.meta.descriptors.MetaDescriptor -import space.kscience.dataforge.misc.DFExperimental +import space.kscience.dataforge.meta.get import space.kscience.dataforge.names.* import space.kscience.dataforge.values.Value import space.kscience.visionforge.* @@ -17,6 +15,18 @@ public interface SolidReference : VisionGroup { * The prototype for this reference. Always returns a "real" prototype, not a reference */ public val prototype: Solid + + override fun getPropertyValue(name: Name, inherit: Boolean, includeStyles: Boolean, includeDefaults: Boolean): Value? { + meta[name]?.value?.let { return it } + if (includeStyles) { + getStyleProperty(name)?.let { return it } + } + prototype.getPropertyValue(name, inherit, includeStyles, includeDefaults)?.let { return it } + if (inherit) { + parent?.getPropertyValue(name, inherit, includeStyles, includeDefaults)?.let { return it } + } + return null + } } @@ -31,27 +41,6 @@ public val Vision.unref: Solid else -> error("This Vision is neither Solid nor SolidReference") } - -private fun SolidReference.getRefProperty( - name: Name, - inherit: Boolean, - includeStyles: Boolean, - includeDefaults: Boolean, -): Meta? = if (!inherit && !includeStyles && !includeDefaults) { - getOwnProperty(name) -} else { - buildList { - add(getOwnProperty(name)) - if (includeStyles) { - addAll(getStyleItems(name)) - } - add(prototype.getProperty(name, inherit, includeStyles, includeDefaults)) - if (inherit) { - add(parent?.getProperty(name, inherit)) - } - }.merge() -} - private fun childToken(childName: Name): NameToken = NameToken(SolidReferenceGroup.REFERENCE_CHILD_PROPERTY_PREFIX, childName.toString()) @@ -83,12 +72,8 @@ public class SolidReferenceGroup( ReferenceChild(this, it.key.asName()) } ?: emptyMap() - override fun getProperty( - name: Name, - inherit: Boolean, - includeStyles: Boolean, - includeDefaults: Boolean, - ): Meta? = getRefProperty(name, inherit, includeStyles, includeDefaults) + override fun getPropertyValue(name: Name, inherit: Boolean, includeStyles: Boolean, includeDefaults: Boolean): Value? = + super.getPropertyValue(name, inherit, includeStyles, includeDefaults) override val descriptor: MetaDescriptor get() = prototype.descriptor @@ -111,6 +96,10 @@ public class SolidReferenceGroup( } } + override val meta: ObservableMutableMeta by lazy { + owner.meta.getOrCreate(childToken(refName).asName()) + } + override val children: Map get() = (prototype as? VisionGroup)?.children ?.filter { it.key != SolidGroup.PROTOTYPES_TOKEN } @@ -118,24 +107,6 @@ public class SolidReferenceGroup( ReferenceChild(owner, refName + key.asName()) } ?: emptyMap() - override fun getOwnProperty(name: Name): Meta? = - owner.getOwnProperty(childPropertyName(refName, name)) - - override fun setPropertyNode(name: Name, node: Meta?, notify: Boolean) { - owner.setPropertyNode(childPropertyName(refName, name), node, notify) - } - - override fun setPropertyValue(name: Name, value: Value?, notify: Boolean) { - owner.setPropertyValue(childPropertyName(refName, name), value, notify) - } - - override fun getProperty( - name: Name, - inherit: Boolean, - includeStyles: Boolean, - includeDefaults: Boolean, - ): Meta? = getRefProperty(name, inherit, includeStyles, includeDefaults) - override var parent: VisionGroup? get() { val parentName = refName.cutLast() @@ -145,21 +116,11 @@ public class SolidReferenceGroup( error("Setting a parent for a reference child is not possible") } - @DFExperimental - override val propertyChanges: Flow - get() = owner.propertyChanges.mapNotNull { name -> - if (name.startsWith(childToken(refName))) { - name.cutFirst() - } else { - null - } - } - override fun invalidateProperty(propertyName: Name) { owner.invalidateProperty(childPropertyName(refName, propertyName)) } - override fun change(change: VisionChange) { + override fun update(change: VisionChange) { change.properties?.let { updateProperties(Name.EMPTY, it) } diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Axes.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Axes.kt index 8f980dad..485cc8bd 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Axes.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Axes.kt @@ -4,8 +4,8 @@ import space.kscience.dataforge.meta.Scheme import space.kscience.dataforge.meta.SchemeSpec import space.kscience.dataforge.meta.boolean import space.kscience.dataforge.meta.descriptors.MetaDescriptor +import space.kscience.dataforge.meta.descriptors.value import space.kscience.dataforge.meta.double -import space.kscience.visionforge.value public class Axes : Scheme() { public var visible: Boolean by boolean(false) diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Camera.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Camera.kt index 2319fa69..b8cb05d4 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Camera.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Camera.kt @@ -3,9 +3,9 @@ package space.kscience.visionforge.solid.specifications import space.kscience.dataforge.meta.Scheme import space.kscience.dataforge.meta.SchemeSpec import space.kscience.dataforge.meta.descriptors.MetaDescriptor +import space.kscience.dataforge.meta.descriptors.value import space.kscience.dataforge.meta.double import space.kscience.dataforge.meta.int -import space.kscience.visionforge.value import kotlin.math.PI public class Camera : Scheme() { @@ -27,24 +27,24 @@ public class Camera : Scheme() { public const val FAR_CLIP: Double = 10000.0 public const val FIELD_OF_VIEW: Int = 75 - override val descriptor: MetaDescriptor by lazy { + override val descriptor: MetaDescriptor by lazy { MetaDescriptor { - value(Camera::fov){ + value(Camera::fov) { default(FIELD_OF_VIEW) } - value(Camera::nearClip){ + value(Camera::nearClip) { default(NEAR_CLIP) } - value(Camera::farClip){ + value(Camera::farClip) { default(FAR_CLIP) } - value(Camera::distance){ + value(Camera::distance) { default(INITIAL_DISTANCE) } - value(Camera::azimuth){ + value(Camera::azimuth) { default(INITIAL_AZIMUTH) } - value(Camera::latitude){ + value(Camera::latitude) { default(INITIAL_LATITUDE) } } diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Canvas3DOptions.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Canvas3DOptions.kt index ea546821..01f6e74e 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Canvas3DOptions.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Canvas3DOptions.kt @@ -2,11 +2,11 @@ package space.kscience.visionforge.solid.specifications import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.descriptors.MetaDescriptor +import space.kscience.dataforge.meta.descriptors.scheme +import space.kscience.dataforge.meta.descriptors.value import space.kscience.dataforge.names.Name import space.kscience.dataforge.values.ValueType import space.kscience.visionforge.hide -import space.kscience.visionforge.scheme -import space.kscience.visionforge.value import space.kscience.visionforge.widgetType public class Clipping : Scheme() { @@ -80,7 +80,19 @@ public class Canvas3DOptions : Scheme() { override val descriptor: MetaDescriptor by lazy { MetaDescriptor { scheme(Canvas3DOptions::axes, Axes) - scheme(Canvas3DOptions::light, Light) + + value(Canvas3DOptions::layers) { + multiple = true + default(listOf(0)) + widgetType = "multiSelect" + allowedValues(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + } + + scheme(Canvas3DOptions::clipping, Clipping) + + scheme(Canvas3DOptions::light, Light){ + hide() + } scheme(Canvas3DOptions::camera, Camera) { hide() @@ -93,15 +105,6 @@ public class Canvas3DOptions : Scheme() { scheme(Canvas3DOptions::size, CanvasSize) { hide() } - - value(Canvas3DOptions::layers) { - type(ValueType.NUMBER) - multiple = true - default(listOf(0)) - widgetType = "multiSelect" - allowedValues(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10) - } - scheme(Canvas3DOptions::clipping, Clipping) } } } diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/transform/RemoveSingleChild.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/transform/RemoveSingleChild.kt index e623ef46..39aadbf1 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/transform/RemoveSingleChild.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/transform/RemoveSingleChild.kt @@ -1,5 +1,6 @@ package space.kscience.visionforge.solid.transform +import space.kscience.dataforge.meta.configure import space.kscience.dataforge.meta.update import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.names.asName @@ -22,7 +23,7 @@ internal fun Vision.updateFrom(other: Vision): Vision { scaleY *= other.scaleY scaleZ *= other.scaleZ configure{ - update(other.meta()) + update(other.meta) } } return this diff --git a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/PropertyTest.kt b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/PropertyTest.kt index a31ed9f8..d146195a 100644 --- a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/PropertyTest.kt +++ b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/PropertyTest.kt @@ -1,13 +1,24 @@ package space.kscience.visionforge.solid -import space.kscience.dataforge.meta.int +import space.kscience.dataforge.meta.* import space.kscience.dataforge.names.asName +import space.kscience.dataforge.values.int import space.kscience.visionforge.* import kotlin.test.Test import kotlin.test.assertEquals @Suppress("UNUSED_VARIABLE") class PropertyTest { + @Test + fun testColorUpdate(){ + val box = Box(10.0f, 10.0f,10.0f) + box.material { + //meta["color"] = "pink" + color("pink") + } + assertEquals("pink", box.meta["material.color"]?.string) + } + @Test fun testInheritedProperty() { var box: Box? = null @@ -17,7 +28,7 @@ class PropertyTest { box = box(100, 100, 100) } } - assertEquals(22, box?.getProperty("test", inherit = true).int) + assertEquals(22, box?.getPropertyValue("test", inherit = true)?.int) } @Test @@ -35,11 +46,11 @@ class PropertyTest { } } } - assertEquals(22, box?.getProperty("test").int) + assertEquals(22, box?.getPropertyValue("test")?.int) } @Test - fun testColor() { + fun testStyleColor() { var box: Box? = null val group = SolidGroup().apply { styleSheet { diff --git a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/SerializationTest.kt b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/SerializationTest.kt index d878881f..8f5c3f57 100644 --- a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/SerializationTest.kt +++ b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/SerializationTest.kt @@ -3,7 +3,6 @@ package space.kscience.visionforge.solid import space.kscience.dataforge.names.Name import space.kscience.visionforge.MutableVisionGroup import space.kscience.visionforge.get -import space.kscience.visionforge.meta import kotlin.test.Test import kotlin.test.assertEquals @@ -32,7 +31,7 @@ class SerializationTest { val string = Solids.encodeToString(cube) println(string) val newCube = Solids.decodeFromString(string) - assertEquals(cube.meta(), newCube.meta()) + assertEquals(cube.meta, newCube.meta) } @Test @@ -53,7 +52,7 @@ class SerializationTest { val string = Solids.encodeToString(group) println(string) val reconstructed = Solids.decodeFromString(string) as SolidGroup - assertEquals(group["cube"]?.meta(), reconstructed["cube"]?.meta()) + assertEquals(group["cube"]?.meta, reconstructed["cube"]?.meta) } } \ No newline at end of file diff --git a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/VisionUpdateTest.kt b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/VisionUpdateTest.kt index 821e7f04..7a36ab3f 100644 --- a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/VisionUpdateTest.kt +++ b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/VisionUpdateTest.kt @@ -28,7 +28,7 @@ class VisionUpdateTest { propertyChanged("top".asName(), SolidMaterial.MATERIAL_COLOR_KEY, Meta("red".asValue())) propertyChanged("origin".asName(), SolidMaterial.MATERIAL_COLOR_KEY, Meta("red".asValue())) } - targetVision.change(dif) + targetVision.update(dif) assertTrue { targetVision["top"] is SolidGroup } assertEquals("red", (targetVision["origin"] as Solid).color.string) // Should work assertEquals("#00007b", (targetVision["top"] as Solid).color.string) // new item always takes precedence diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/MeshThreeFactory.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/MeshThreeFactory.kt index 7e90a657..992035a6 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/MeshThreeFactory.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/MeshThreeFactory.kt @@ -4,12 +4,13 @@ import info.laht.threekt.core.BufferGeometry import info.laht.threekt.geometries.EdgesGeometry import info.laht.threekt.objects.LineSegments import info.laht.threekt.objects.Mesh -import space.kscience.dataforge.meta.boolean -import space.kscience.dataforge.meta.node +import space.kscience.dataforge.meta.get import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.plus import space.kscience.dataforge.names.startsWith +import space.kscience.dataforge.values.boolean +import space.kscience.visionforge.computeProperties import space.kscience.visionforge.onPropertyChange import space.kscience.visionforge.solid.Solid import space.kscience.visionforge.solid.SolidMaterial @@ -40,7 +41,7 @@ public abstract class MeshThreeFactory( } //add listener to object properties - obj.onPropertyChange(three.updateScope) { name -> + obj.onPropertyChange { name -> when { name.startsWith(Solid.GEOMETRY_KEY) -> { val oldGeometry = mesh.geometry as BufferGeometry @@ -83,14 +84,10 @@ internal fun Mesh.applyProperties(obj: Solid): Mesh = apply { public fun Mesh.applyEdges(obj: Solid) { val edges = children.find { it.name == "@edges" } as? LineSegments //inherited edges definition, enabled by default - if (obj.getProperty(MeshThreeFactory.EDGES_ENABLED_KEY, inherit = true, includeStyles = true).boolean != false) { + if (obj.getPropertyValue(MeshThreeFactory.EDGES_ENABLED_KEY, inherit = true, includeStyles = true)?.boolean != false) { val bufferGeometry = geometry as? BufferGeometry ?: return val material = ThreeMaterials.getLineMaterial( - obj.getProperty( - MeshThreeFactory.EDGES_MATERIAL_KEY, - inherit = true, - includeStyles = true - ), + obj.computeProperties().get(MeshThreeFactory.EDGES_MATERIAL_KEY), true ) if (edges == null) { diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCompositeFactory.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCompositeFactory.kt index 2cdd12d3..5705459a 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCompositeFactory.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCompositeFactory.kt @@ -48,7 +48,7 @@ public class ThreeCompositeFactory(public val three: ThreePlugin) : ThreeFactory }.apply { updatePosition(obj) applyProperties(obj) - obj.onPropertyChange(three.updateScope) { name -> + obj.onPropertyChange { name -> when { //name.startsWith(WIREFRAME_KEY) -> mesh.applyWireFrame(obj) name.startsWith(MeshThreeFactory.EDGES_KEY) -> applyEdges(obj) diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeGeometryBuilder.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeGeometryBuilder.kt index c1c5aa72..fc226de6 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeGeometryBuilder.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeGeometryBuilder.kt @@ -40,7 +40,7 @@ public class ThreeGeometryBuilder : GeometryBuilder { } override fun face(vertex1: Point3D, vertex2: Point3D, vertex3: Point3D, normal: Point3D?, meta: Meta) { - val actualNormal: Point3D = normal ?: (vertex3 - vertex2) cross (vertex1 - vertex2) + val actualNormal: Point3D = normal ?: ((vertex3 - vertex2) cross (vertex1 - vertex2)) indices.add( vertex(vertex1, actualNormal), vertex(vertex2, actualNormal), diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeLabelFactory.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeLabelFactory.kt index d0c10605..34ee5ed1 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeLabelFactory.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeLabelFactory.kt @@ -27,7 +27,7 @@ public object ThreeLabelFactory : ThreeFactory { return Mesh(textGeo, ThreeMaterials.DEFAULT).apply { updateMaterial(obj) updatePosition(obj) - obj.onPropertyChange(three.updateScope) { _ -> + obj.onPropertyChange { _ -> //TODO three.logger.warn { "Label parameter change not implemented" } } diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeLineFactory.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeLineFactory.kt index bc369086..49fca0d9 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeLineFactory.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeLineFactory.kt @@ -4,6 +4,7 @@ import info.laht.threekt.core.BufferGeometry import info.laht.threekt.core.Object3D import info.laht.threekt.math.Color import info.laht.threekt.objects.LineSegments +import space.kscience.visionforge.computePropertyNode import space.kscience.visionforge.onPropertyChange import space.kscience.visionforge.solid.PolyLine import space.kscience.visionforge.solid.color @@ -20,7 +21,7 @@ public object ThreeLineFactory : ThreeFactory { } val material = ThreeMaterials.getLineMaterial( - obj.getProperty(MeshThreeFactory.EDGES_MATERIAL_KEY), + obj.computePropertyNode(MeshThreeFactory.EDGES_MATERIAL_KEY), true ) @@ -31,7 +32,7 @@ public object ThreeLineFactory : ThreeFactory { updatePosition(obj) //layers.enable(obj.layer) //add listener to object properties - obj.onPropertyChange(three.updateScope) { propertyName -> + obj.onPropertyChange { propertyName -> updateProperty(obj, propertyName) } } diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeMaterials.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeMaterials.kt index 258df5ff..9a3c6be9 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeMaterials.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeMaterials.kt @@ -8,12 +8,10 @@ import info.laht.threekt.math.Color import info.laht.threekt.objects.Mesh import space.kscience.dataforge.meta.* import space.kscience.dataforge.names.Name -import space.kscience.dataforge.values.ValueType -import space.kscience.dataforge.values.int -import space.kscience.dataforge.values.string +import space.kscience.dataforge.values.* import space.kscience.visionforge.Colors import space.kscience.visionforge.Vision -import space.kscience.visionforge.meta +import space.kscience.visionforge.computePropertyNode import space.kscience.visionforge.solid.SolidMaterial @@ -117,8 +115,8 @@ private var Material.cached: Boolean public fun Mesh.updateMaterial(vision: Vision) { //val meta = vision.getProperty(SolidMaterial.MATERIAL_KEY, inherit = true).node - val ownMaterialMeta = vision.meta()[SolidMaterial.MATERIAL_KEY] - val parentMaterialMeta = vision.parent?.getProperty( + val ownMaterialMeta = vision.meta.getMeta(SolidMaterial.MATERIAL_KEY) + val parentMaterialMeta = vision.parent?.getPropertyValue( SolidMaterial.MATERIAL_KEY, inherit = true, includeStyles = false, @@ -128,19 +126,15 @@ public fun Mesh.updateMaterial(vision: Vision) { material = when { ownMaterialMeta == null && parentMaterialMeta == null -> { //If material is style-based, use cached - vision.getProperty( + vision.computePropertyNode( SolidMaterial.MATERIAL_KEY, - inherit = false, - includeStyles = true, - includeDefaults = false )?.let { ThreeMaterials.cacheMaterial(it) } ?: ThreeMaterials.DEFAULT } else -> { - vision.getProperty( + vision.computePropertyNode( SolidMaterial.MATERIAL_KEY, - inherit = true )?.let { ThreeMaterials.buildMaterial(it) } ?: ThreeMaterials.DEFAULT @@ -155,32 +149,29 @@ public fun Mesh.updateMaterialProperty(vision: Vision, propertyName: Name) { } else { when (propertyName) { SolidMaterial.MATERIAL_COLOR_KEY -> { - material.asDynamic().color = vision.getProperty( + material.asDynamic().color = vision.computePropertyNode( SolidMaterial.MATERIAL_COLOR_KEY, - inherit = true, - includeStyles = true, - includeDefaults = false )?.threeColor() ?: ThreeMaterials.DEFAULT_COLOR material.needsUpdate = true } SolidMaterial.MATERIAL_OPACITY_KEY -> { - val opacity = vision.getProperty( + val opacity = vision.getPropertyValue( SolidMaterial.MATERIAL_OPACITY_KEY, inherit = true, includeStyles = true, includeDefaults = false - ).double ?: 1.0 + )?.double ?: 1.0 material.opacity = opacity material.transparent = opacity < 1.0 material.needsUpdate = true } SolidMaterial.MATERIAL_WIREFRAME_KEY -> { - material.asDynamic().wireframe = vision.getProperty( + material.asDynamic().wireframe = vision.getPropertyValue( SolidMaterial.MATERIAL_WIREFRAME_KEY, inherit = true, includeStyles = true, includeDefaults = false - ).boolean ?: false + )?.boolean ?: false material.needsUpdate = true } else -> console.warn("Unrecognized material property: $propertyName") diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreePlugin.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreePlugin.kt index 9c7c1f6b..e7c2ac86 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreePlugin.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreePlugin.kt @@ -69,7 +69,7 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer { updatePosition(obj) //obj.onChildrenChange() - obj.onPropertyChange(updateScope) { name -> + obj.onPropertyChange { name -> if ( name.startsWith(Solid.POSITION_KEY) || name.startsWith(Solid.ROTATION_KEY) || diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeReferenceFactory.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeReferenceFactory.kt index 4dca0eeb..53e9c2c9 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeReferenceFactory.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeReferenceFactory.kt @@ -47,7 +47,7 @@ public object ThreeReferenceFactory : ThreeFactory { //TODO apply child properties - obj.onPropertyChange(three.updateScope) { name-> + obj.onPropertyChange { name-> if (name.firstOrNull()?.body == REFERENCE_CHILD_PROPERTY_PREFIX) { val childName = name.firstOrNull()?.index?.let(Name::parse) ?: error("Wrong syntax for reference child property: '$name'") val propertyName = name.cutFirst() diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/csg.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/csg.kt index 50e48ee9..ac2fefc0 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/csg.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/csg.kt @@ -3,7 +3,8 @@ "OVERRIDING_FINAL_MEMBER", "RETURN_TYPE_MISMATCH_ON_OVERRIDE", "CONFLICTING_OVERLOADS", - "EXTERNAL_DELEGATION" + "EXTERNAL_DELEGATION", + "NO_EXPLICIT_VISIBILITY_IN_API_MODE_WARNING" ) @file:JsModule("three-csg-ts") From 5721bb9456273fa37b2189bffe3cf182c9f9bcc7 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Wed, 11 Aug 2021 16:52:36 +0300 Subject: [PATCH 05/15] A lot of fixes --- build.gradle.kts | 3 +- .../visionforge/gdml/GDMLVisionTest.kt | 27 ++-- .../visionforge/gdml/demo/GdmlFxDemoApp.kt | 5 +- .../kotlin/ru/mipt/npm/muon/monitor/Model.kt | 5 + .../ru/mipt/npm/muon/monitor/Monitor.kt | 3 +- .../mipt/npm/muon/monitor/MMAppComponent.kt | 7 +- .../ru/mipt/npm/muon/monitor/MMDemoApp.kt | 16 ++- .../kscience/visionforge/solid/demo/demo.kt | 3 +- .../visionforge/solid/demo/FXDemoApp.kt | 2 +- .../visionforge/solid/demo/MetaEditorDemo.kt | 7 +- .../visionforge/bootstrap/outputConfig.kt | 6 +- .../visionforge/bootstrap/threeControls.kt | 2 +- .../kscience/visionforge/react/MetaViewer.kt | 1 - .../visionforge/react/PropertyEditor.kt | 2 +- .../ringThreeControls.kt | 1 - .../visionforge/ComputedVisionProperties.kt | 83 ++++++++---- .../space/kscience/visionforge/StyleSheet.kt | 5 +- .../space/kscience/visionforge/VisionBase.kt | 40 +++--- .../space/kscience/visionforge/VisionGroup.kt | 3 + .../kotlin/space/kscience/visionforge/misc.kt | 17 --- .../kscience/visionforge/visionDescriptor.kt | 22 +-- .../kscience/visionforge/html/HtmlTagTest.kt | 3 - visionforge-fx/build.gradle.kts | 9 +- .../visionforge/editor/FXMetaModel.kt | 31 +++-- .../kscience/visionforge/editor/MetaViewer.kt | 6 +- .../visionforge/editor/MutableMetaEditor.kt | 21 ++- .../editor/VisionEditorFragment.kt | 19 ++- .../kscience/visionforge/solid/FX3DPlugin.kt | 4 +- .../kscience/visionforge/solid/FXMaterials.kt | 34 +++-- .../visionforge/solid/FXReferenceFactory.kt | 2 +- .../visionforge/gdml/GdmlTransformer.kt | 14 -- .../visionforge/plotly/VisionOfPlotly.kt | 1 - .../visionforge/solid/ColorAccessor.kt | 23 ++-- .../kscience/visionforge/solid/Composite.kt | 49 ++++--- .../kscience/visionforge/solid/SolidBase.kt | 4 + .../kscience/visionforge/solid/SolidGroup.kt | 4 + .../visionforge/solid/SolidMaterial.kt | 14 +- .../visionforge/solid/SolidReference.kt | 29 +++- .../visionforge/solid/PropertyTest.kt | 24 +++- .../solid/three/MeshThreeFactory.kt | 20 ++- .../solid/three/ThreeCanvasLabelFactory.kt | 4 +- .../visionforge/solid/three/ThreeMaterials.kt | 127 ++++++++---------- .../solid/three/ThreeReferenceFactory.kt | 3 +- 43 files changed, 382 insertions(+), 323 deletions(-) delete mode 100644 visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/misc.kt diff --git a/build.gradle.kts b/build.gradle.kts index ec691447..08ad66d8 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,14 +2,13 @@ plugins { id("ru.mipt.npm.gradle.project") } -val dataforgeVersion by extra("0.5.0-dev-10") +val dataforgeVersion by extra("0.5.0-dev-11") val fxVersion by extra("11") allprojects { repositories { mavenLocal() mavenCentral() - jcenter() maven("https://repo.kotlin.link") maven("https://maven.jzy3d.org/releases") } diff --git a/demo/gdml/src/commonTest/kotlin/space/kscience/visionforge/gdml/GDMLVisionTest.kt b/demo/gdml/src/commonTest/kotlin/space/kscience/visionforge/gdml/GDMLVisionTest.kt index 50ba6d12..12918532 100644 --- a/demo/gdml/src/commonTest/kotlin/space/kscience/visionforge/gdml/GDMLVisionTest.kt +++ b/demo/gdml/src/commonTest/kotlin/space/kscience/visionforge/gdml/GDMLVisionTest.kt @@ -4,27 +4,36 @@ import space.kscience.dataforge.names.Name import space.kscience.dataforge.values.asValue import space.kscience.dataforge.values.string import space.kscience.gdml.GdmlShowCase +import space.kscience.visionforge.Vision +import space.kscience.visionforge.computeProperties +import space.kscience.visionforge.get import space.kscience.visionforge.setProperty +import space.kscience.visionforge.solid.Solid import space.kscience.visionforge.solid.SolidMaterial +import space.kscience.visionforge.solid.material import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNotNull class GDMLVisionTest { + private val cubes = GdmlShowCase.cubes().toVision() -// @Test -// fun testCubesStyles(){ -// val cubes = gdml.toVision() -// val segment = cubes["composite000.segment_0".toName()] as Solid -// println(segment.styles) -// println(segment.material) -// } + @Test + fun testCubesStyles(){ + val segment = cubes["composite-000.segment-0"] as Solid + println(segment.computeProperties().getValue(Vision.STYLE_KEY)) +// println(segment.computePropertyNode(SolidMaterial.MATERIAL_KEY)) +// println(segment.computeProperty(SolidMaterial.MATERIAL_COLOR_KEY)) + + println(segment.material?.meta) + + //println(Solids.encodeToString(cubes)) + } @Test fun testPrototypeProperty() { - val vision = GdmlShowCase.cubes().toVision() - val child = vision[Name.of("composite-000","segment-0")] + val child = cubes[Name.of("composite-000","segment-0")] assertNotNull(child) child.setProperty(SolidMaterial.MATERIAL_COLOR_KEY, "red".asValue()) assertEquals("red", child.getPropertyValue(SolidMaterial.MATERIAL_COLOR_KEY)?.string) diff --git a/demo/gdml/src/jvmMain/kotlin/space/kscience/visionforge/gdml/demo/GdmlFxDemoApp.kt b/demo/gdml/src/jvmMain/kotlin/space/kscience/visionforge/gdml/demo/GdmlFxDemoApp.kt index cb40431c..f2371f44 100644 --- a/demo/gdml/src/jvmMain/kotlin/space/kscience/visionforge/gdml/demo/GdmlFxDemoApp.kt +++ b/demo/gdml/src/jvmMain/kotlin/space/kscience/visionforge/gdml/demo/GdmlFxDemoApp.kt @@ -7,7 +7,6 @@ import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.fetch import space.kscience.gdml.GdmlShowCase import space.kscience.visionforge.VisionManager -import space.kscience.visionforge.computeProperties import space.kscience.visionforge.editor.VisionEditorFragment import space.kscience.visionforge.editor.VisionTreeFragment import space.kscience.visionforge.gdml.toVision @@ -33,9 +32,7 @@ class GDMLView : View() { this.itemProperty.bind(canvas.rootObjectProperty) } - private val propertyEditor = VisionEditorFragment { - it.computeProperties() - }.apply { + private val propertyEditor = VisionEditorFragment().apply { descriptorProperty.set(SolidMaterial.descriptor) visionProperty.bind(treeFragment.selectedProperty) } diff --git a/demo/muon-monitor/src/commonMain/kotlin/ru/mipt/npm/muon/monitor/Model.kt b/demo/muon-monitor/src/commonMain/kotlin/ru/mipt/npm/muon/monitor/Model.kt index 87065723..854e520c 100644 --- a/demo/muon-monitor/src/commonMain/kotlin/ru/mipt/npm/muon/monitor/Model.kt +++ b/demo/muon-monitor/src/commonMain/kotlin/ru/mipt/npm/muon/monitor/Model.kt @@ -28,6 +28,7 @@ class Model(val manager: VisionManager) { private fun SolidGroup.detector(detector: SC16) { group(detector.name) { + position = detector.center detector.pixels.forEach { pixel(it) } @@ -38,6 +39,10 @@ class Model(val manager: VisionManager) { val root: SolidGroup = SolidGroup().apply { root(this@Model.manager) + material { + wireframe + color("darkgreen") + } rotationX = PI / 2 group("bottom") { Monitor.detectors.filter { it.center.z == LOWER_LAYER_Z }.forEach { diff --git a/demo/muon-monitor/src/commonMain/kotlin/ru/mipt/npm/muon/monitor/Monitor.kt b/demo/muon-monitor/src/commonMain/kotlin/ru/mipt/npm/muon/monitor/Monitor.kt index 17d5ac86..3b64bb5a 100644 --- a/demo/muon-monitor/src/commonMain/kotlin/ru/mipt/npm/muon/monitor/Monitor.kt +++ b/demo/muon-monitor/src/commonMain/kotlin/ru/mipt/npm/muon/monitor/Monitor.kt @@ -3,7 +3,6 @@ package ru.mipt.npm.muon.monitor import ru.mipt.npm.muon.monitor.Monitor.PIXEL_XY_SIZE import ru.mipt.npm.muon.monitor.Monitor.PIXEL_Z_SIZE import space.kscience.visionforge.solid.Point3D -import space.kscience.visionforge.solid.plus /** * A single pixel @@ -98,7 +97,7 @@ class SC16( } val offset = Point3D(-y, x, 0)//rotateDetector(Point3D(x, y, 0.0)); val pixelName = "${name}_${index}" - SC1(pixelName, center + offset) + SC1(pixelName, offset) } } } diff --git a/demo/muon-monitor/src/jsMain/kotlin/ru/mipt/npm/muon/monitor/MMAppComponent.kt b/demo/muon-monitor/src/jsMain/kotlin/ru/mipt/npm/muon/monitor/MMAppComponent.kt index 75b610ac..d981fa44 100644 --- a/demo/muon-monitor/src/jsMain/kotlin/ru/mipt/npm/muon/monitor/MMAppComponent.kt +++ b/demo/muon-monitor/src/jsMain/kotlin/ru/mipt/npm/muon/monitor/MMAppComponent.kt @@ -2,6 +2,7 @@ package ru.mipt.npm.muon.monitor import io.ktor.client.HttpClient import io.ktor.client.request.get +import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import kotlinx.css.* @@ -23,6 +24,7 @@ import space.kscience.visionforge.react.flexColumn import space.kscience.visionforge.react.visionTree import space.kscience.visionforge.solid.specifications.Camera import space.kscience.visionforge.solid.specifications.Canvas3DOptions +import space.kscience.visionforge.solid.three.edges import styled.css import styled.styledDiv import kotlin.math.PI @@ -34,6 +36,7 @@ external interface MMAppProps : RProps { var selected: Name? } +@OptIn(DelicateCoroutinesApi::class) @JsExport val MMApp = functionalComponent("Muon monitor") { props -> var selected by useState { props.selected } @@ -53,7 +56,9 @@ val MMApp = functionalComponent("Muon monitor") { props -> } } - val root = props.model.root + val root = props.model.root.apply { + edges() + } gridRow { flexColumn { diff --git a/demo/muon-monitor/src/jsMain/kotlin/ru/mipt/npm/muon/monitor/MMDemoApp.kt b/demo/muon-monitor/src/jsMain/kotlin/ru/mipt/npm/muon/monitor/MMDemoApp.kt index 5c4a589a..9daa6213 100644 --- a/demo/muon-monitor/src/jsMain/kotlin/ru/mipt/npm/muon/monitor/MMDemoApp.kt +++ b/demo/muon-monitor/src/jsMain/kotlin/ru/mipt/npm/muon/monitor/MMDemoApp.kt @@ -7,18 +7,15 @@ import kotlinx.browser.document import react.child import react.dom.render import space.kscience.dataforge.context.Context -import space.kscience.dataforge.context.Global import space.kscience.dataforge.context.fetch import space.kscience.visionforge.Application import space.kscience.visionforge.VisionManager import space.kscience.visionforge.bootstrap.useBootstrap +import space.kscience.visionforge.solid.three.ThreePlugin import space.kscience.visionforge.startApplication private class MMDemoApp : Application { - private val visionManager = Global.fetch(VisionManager) - private val model = Model(visionManager) - private val connection = HttpClient { install(JsonFeature) { serializer = KotlinxSerializer() @@ -28,13 +25,18 @@ private class MMDemoApp : Application { override fun start(state: Map) { useBootstrap() - val element = document.getElementById("app") ?: error("Element with id 'app' not found on page") + val context = Context("MM-demo"){ + plugin(ThreePlugin) + } + val visionManager = context.fetch(VisionManager) - val context = Context("demo") + val model = Model(visionManager) + + val element = document.getElementById("app") ?: error("Element with id 'app' not found on page") render(element) { child(MMApp) { attrs { - this.model = this@MMDemoApp.model + this.model = model this.connection = this@MMDemoApp.connection this.context = context } diff --git a/demo/solid-showcase/src/commonMain/kotlin/space/kscience/visionforge/solid/demo/demo.kt b/demo/solid-showcase/src/commonMain/kotlin/space/kscience/visionforge/solid/demo/demo.kt index c933c3ce..2f533c95 100644 --- a/demo/solid-showcase/src/commonMain/kotlin/space/kscience/visionforge/solid/demo/demo.kt +++ b/demo/solid-showcase/src/commonMain/kotlin/space/kscience/visionforge/solid/demo/demo.kt @@ -19,7 +19,7 @@ fun VisionLayout.demo(name: String, title: String = name, block: SolidGro "title" put title } val vision = SolidGroup(block) - render(Name.parse(name), vision) + render(Name.parse(name), vision, meta) } val canvasOptions = Canvas3DOptions { @@ -36,6 +36,7 @@ val canvasOptions = Canvas3DOptions { } } +@OptIn(DelicateCoroutinesApi::class) fun VisionLayout.showcase() { demo("shapes", "Basic shapes") { box(100.0, 100.0, 100.0) { diff --git a/demo/solid-showcase/src/jvmMain/kotlin/space/kscience/visionforge/solid/demo/FXDemoApp.kt b/demo/solid-showcase/src/jvmMain/kotlin/space/kscience/visionforge/solid/demo/FXDemoApp.kt index 3ce92f0f..7f278d28 100644 --- a/demo/solid-showcase/src/jvmMain/kotlin/space/kscience/visionforge/solid/demo/FXDemoApp.kt +++ b/demo/solid-showcase/src/jvmMain/kotlin/space/kscience/visionforge/solid/demo/FXDemoApp.kt @@ -14,7 +14,7 @@ class FXDemoApp : App(FXDemoGrid::class) { stage.height = 600.0 view.showcase() - view.showcaseCSG() + //view.showcaseCSG() } } diff --git a/demo/solid-showcase/src/jvmMain/kotlin/space/kscience/visionforge/solid/demo/MetaEditorDemo.kt b/demo/solid-showcase/src/jvmMain/kotlin/space/kscience/visionforge/solid/demo/MetaEditorDemo.kt index b00ef055..3cdf058e 100644 --- a/demo/solid-showcase/src/jvmMain/kotlin/space/kscience/visionforge/solid/demo/MetaEditorDemo.kt +++ b/demo/solid-showcase/src/jvmMain/kotlin/space/kscience/visionforge/solid/demo/MetaEditorDemo.kt @@ -1,7 +1,6 @@ package space.kscience.visionforge.demo import javafx.geometry.Orientation -import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.MutableMeta import space.kscience.dataforge.meta.descriptors.MetaDescriptor import space.kscience.dataforge.meta.descriptors.node @@ -46,12 +45,12 @@ class MetaEditorDemo : View("Meta editor demo") { } } - private val rootNode = FXMetaModel.root(meta, descriptor) + private val rootNode:FXMetaModel = FXMetaModel.root(meta, descriptor) override val root = splitpane( Orientation.HORIZONTAL, - MetaViewer(rootNode as Meta).root, - MutableMetaEditor(rootNode as FXMetaModel).root + MetaViewer(rootNode).root, + MutableMetaEditor(rootNode).root ) } diff --git a/ui/bootstrap/src/main/kotlin/space/kscience/visionforge/bootstrap/outputConfig.kt b/ui/bootstrap/src/main/kotlin/space/kscience/visionforge/bootstrap/outputConfig.kt index f16985c5..8088d2fd 100644 --- a/ui/bootstrap/src/main/kotlin/space/kscience/visionforge/bootstrap/outputConfig.kt +++ b/ui/bootstrap/src/main/kotlin/space/kscience/visionforge/bootstrap/outputConfig.kt @@ -12,9 +12,9 @@ import org.w3c.files.BlobPropertyBag import react.* import react.dom.attrs import react.dom.button -import space.kscience.dataforge.meta.descriptors.defaultNode import space.kscience.dataforge.meta.withDefault import space.kscience.visionforge.Vision +import space.kscience.visionforge.encodeToString import space.kscience.visionforge.react.flexColumn import space.kscience.visionforge.react.flexRow import space.kscience.visionforge.react.propertyEditor @@ -51,12 +51,12 @@ public val CanvasControls: FunctionComponent = functionalCo border(1.px, BorderStyle.solid, Color.blue) padding(4.px) } - props.vision?.manager?.let { manager -> + props.vision?.let{ vision -> button { +"Export" attrs { onClickFunction = { - val json = manager.encodeToString(props.vision!!) + val json = vision.encodeToString() saveData(it, "object.json", "text/json") { json } diff --git a/ui/bootstrap/src/main/kotlin/space/kscience/visionforge/bootstrap/threeControls.kt b/ui/bootstrap/src/main/kotlin/space/kscience/visionforge/bootstrap/threeControls.kt index e8166f35..95219db1 100644 --- a/ui/bootstrap/src/main/kotlin/space/kscience/visionforge/bootstrap/threeControls.kt +++ b/ui/bootstrap/src/main/kotlin/space/kscience/visionforge/bootstrap/threeControls.kt @@ -21,7 +21,7 @@ public external interface ThreeControlsProps : RProps { } @JsExport -public val ThreeControls: FunctionalComponent = functionalComponent { props -> +public val ThreeControls: FunctionComponent = functionalComponent { props -> tabPane(if (props.selected != null) "Properties" else null) { tab("Canvas") { card("Canvas configuration") { diff --git a/ui/react/src/main/kotlin/space/kscience/visionforge/react/MetaViewer.kt b/ui/react/src/main/kotlin/space/kscience/visionforge/react/MetaViewer.kt index 288251e0..69683bc5 100644 --- a/ui/react/src/main/kotlin/space/kscience/visionforge/react/MetaViewer.kt +++ b/ui/react/src/main/kotlin/space/kscience/visionforge/react/MetaViewer.kt @@ -9,7 +9,6 @@ import react.dom.a import react.dom.attrs import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.descriptors.MetaDescriptor -import space.kscience.dataforge.meta.descriptors.defaultNode import space.kscience.dataforge.meta.descriptors.get import space.kscience.dataforge.meta.get import space.kscience.dataforge.meta.isLeaf diff --git a/ui/react/src/main/kotlin/space/kscience/visionforge/react/PropertyEditor.kt b/ui/react/src/main/kotlin/space/kscience/visionforge/react/PropertyEditor.kt index cdb1bf30..47b0d818 100644 --- a/ui/react/src/main/kotlin/space/kscience/visionforge/react/PropertyEditor.kt +++ b/ui/react/src/main/kotlin/space/kscience/visionforge/react/PropertyEditor.kt @@ -121,7 +121,7 @@ private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) { }?.forEach { add(NameToken(it.key)) } - ownProperty?.items?.keys?.filterNot { it.body.startsWith("@") }?.let { addAll(it) } + //ownProperty?.items?.keys?.filterNot { it.body.startsWith("@") }?.let { addAll(it) } } flexRow { diff --git a/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ringThreeControls.kt b/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ringThreeControls.kt index 1d9626ed..ca5ca00f 100644 --- a/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ringThreeControls.kt +++ b/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ringThreeControls.kt @@ -15,7 +15,6 @@ import react.dom.button import ringui.Island import ringui.SmartTabs import ringui.Tab -import space.kscience.dataforge.meta.descriptors.defaultNode import space.kscience.dataforge.meta.withDefault import space.kscience.dataforge.names.Name import space.kscience.visionforge.Vision diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/ComputedVisionProperties.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/ComputedVisionProperties.kt index 1007eee6..925eecfe 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/ComputedVisionProperties.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/ComputedVisionProperties.kt @@ -1,53 +1,84 @@ package space.kscience.visionforge import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.meta.ObservableMutableMeta 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( - public val vision: Vision, - public val rootName: Name, - public val visionDescriptor: MetaDescriptor -) : ObservableMutableMeta by vision.meta { + val vision: Vision, + val pathName: Name, + val visionDescriptor: MetaDescriptor, + val parentInheritFlag: Boolean?, + val parentStylesFlag: Boolean? +) : Meta { - public val descriptor: MetaDescriptor? = visionDescriptor[rootName] + val descriptor: MetaDescriptor? by lazy { visionDescriptor[pathName] } - override val items: Map + override val items: Map get() { - val metaKeys = vision.meta.items.keys + val metaKeys = vision.meta.getMeta(pathName)?.items?.keys ?: emptySet() val descriptorKeys = descriptor?.children?.map { NameToken(it.key) } ?: emptySet() - return (metaKeys + descriptorKeys).associateWith { getMeta(rootName + it) } + val inheritFlag = descriptor?.inherited ?: parentInheritFlag + val stylesFlag = descriptor?.usesStyles ?: parentStylesFlag + return (metaKeys + descriptorKeys).associateWith { + ComputedVisionProperties( + vision, + pathName + it, + visionDescriptor, + inheritFlag, + stylesFlag + ) + } } - override var value: Value? + override val value: Value? get() { - val inheritFlag = descriptor?.inherited ?: false - val stylesFlag = descriptor?.usesStyles ?: true - return vision.getPropertyValue(rootName, inheritFlag, stylesFlag, true) - } - set(value) { - vision.meta.setValue(rootName, value) + val inheritFlag = descriptor?.inherited ?: parentInheritFlag ?: false + val stylesFlag = descriptor?.usesStyles ?: parentStylesFlag ?: true + return vision.getPropertyValue(pathName, inheritFlag, stylesFlag, true) } - override fun getMeta(name: Name): ObservableMutableMeta = - ComputedVisionProperties(vision, rootName + name, visionDescriptor) - - override fun getOrCreate(name: Name): ObservableMutableMeta = getMeta(name) - - override fun toMeta(): Meta = this + 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): ObservableMutableMeta = - if (descriptor == null) meta else ComputedVisionProperties(this, Name.EMPTY, 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) + } +} -public fun Vision.computePropertyNode(name: Name, descriptor: MetaDescriptor? = this.descriptor): ObservableMutableMeta? = - computeProperties(descriptor)[name] \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/StyleSheet.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/StyleSheet.kt index dacc09b1..52deee3c 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/StyleSheet.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/StyleSheet.kt @@ -7,6 +7,7 @@ import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.plus import space.kscience.dataforge.values.Value import space.kscience.dataforge.values.asValue +import space.kscience.dataforge.values.stringList import kotlin.jvm.JvmInline /** @@ -72,7 +73,7 @@ internal fun Vision.styleChanged(key: String, oldStyle: Meta?, newStyle: Meta?) * List of names of styles applied to this object. Order matters. Not inherited. */ public var Vision.styles: List - get() = meta.getMeta(Vision.STYLE_KEY)?.stringList ?: emptyList() + get() = meta.getValue(Vision.STYLE_KEY)?.stringList ?: emptyList() set(value) { meta.setValue(Vision.STYLE_KEY, value.map { it.asValue() }.asValue()) } @@ -105,7 +106,7 @@ public fun Vision.getStyleProperty(name: Name): Value? = styles.firstNotNullOfOr /** * Resolve an item in all style layers */ -public fun Vision.getStyleItems(name: Name): List = styles.mapNotNull { +public fun Vision.getStyleNodes(name: Name): List = styles.mapNotNull { getStyle(it)?.get(name) } diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionBase.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionBase.kt index c123938d..77a682aa 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionBase.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionBase.kt @@ -7,7 +7,6 @@ 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.defaultNode import space.kscience.dataforge.meta.descriptors.value import space.kscience.dataforge.meta.get import space.kscience.dataforge.misc.DFExperimental @@ -30,9 +29,11 @@ internal data class MetaListener( @SerialName("vision") public open class VisionBase( @Transient override var parent: VisionGroup? = null, - protected var properties: MutableMeta? = null ) : Vision { + @Transient + protected open var properties: MutableMeta? = null + @Synchronized protected fun getOrCreateProperties(): MutableMeta { if (properties == null) { @@ -45,50 +46,55 @@ public open class VisionBase( @Transient private val listeners = HashSet() - private inner class VisionBaseProperties(val rootName: Name) : ObservableMutableMeta { + private inner class VisionProperties(val pathName: Name) : ObservableMutableMeta { override val items: Map - get() = properties?.get(rootName)?.items?.mapValues { entry -> - VisionBaseProperties(rootName + entry.key) + get() = properties?.get(pathName)?.items?.mapValues { entry -> + VisionProperties(pathName + entry.key) } ?: emptyMap() override var value: Value? - get() = properties?.get(rootName)?.value + get() = properties?.get(pathName)?.value set(value) { - getOrCreateProperties().setValue(rootName, value) + val oldValue = properties?.get(pathName)?.value + getOrCreateProperties().setValue(pathName, value) + if (oldValue != value) { + invalidate(Name.EMPTY) + } } - override fun getOrCreate(name: Name): ObservableMutableMeta = VisionBaseProperties(this.rootName + name) + override fun getOrCreate(name: Name): ObservableMutableMeta = VisionProperties(pathName + name) override fun setMeta(name: Name, node: Meta?) { - getOrCreateProperties().setMeta(name, node) + getOrCreateProperties().setMeta(pathName + name, node) + invalidate(name) } @DFExperimental override fun attach(name: Name, node: ObservableMutableMeta) { val ownProperties = getOrCreateProperties() if (ownProperties is ObservableMutableMeta) { - ownProperties.attach(rootName + name, node) + ownProperties.attach(pathName + name, node) } else { - ownProperties.setMeta(rootName + name, node) + ownProperties.setMeta(pathName + name, node) node.onChange(this) { childName -> - ownProperties.setMeta(rootName + name + childName, this[childName]) + ownProperties.setMeta(pathName + name + childName, this[childName]) } } } override fun invalidate(name: Name) { - invalidateProperty(rootName + name) + invalidateProperty(pathName + name) } @Synchronized override fun onChange(owner: Any?, callback: Meta.(name: Name) -> Unit) { - if (rootName.isEmpty()) { + if (pathName.isEmpty()) { listeners.add((MetaListener(owner, callback))) } else { listeners.add(MetaListener(owner) { name -> - if (name.startsWith(rootName)) { - (get(rootName) ?: Meta.EMPTY).callback(name.removeHeadOrNull(rootName)!!) + if (name.startsWith(pathName)) { + (this@MetaListener[pathName] ?: Meta.EMPTY).callback(name.removeHeadOrNull(pathName)!!) } }) } @@ -104,7 +110,7 @@ public open class VisionBase( override fun hashCode(): Int = Meta.hashCode(this) } - override val meta: ObservableMutableMeta get() = VisionBaseProperties(Name.EMPTY) + override val meta: ObservableMutableMeta get() = VisionProperties(Name.EMPTY) override fun getPropertyValue( name: Name, diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroup.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroup.kt index c7c47901..76640bee 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroup.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroup.kt @@ -4,6 +4,9 @@ import kotlinx.coroutines.flow.Flow import space.kscience.dataforge.names.* import space.kscience.dataforge.provider.Provider +@DslMarker +public annotation class VisionBuilder + public interface VisionContainer { public operator fun get(name: Name): V? } diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/misc.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/misc.kt deleted file mode 100644 index 084416ac..00000000 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/misc.kt +++ /dev/null @@ -1,17 +0,0 @@ -package space.kscience.visionforge - -import space.kscience.dataforge.meta.Laminate -import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.meta.isLeaf - -@DslMarker -public annotation class VisionBuilder - -public fun List.merge(): Meta? { - val first = firstOrNull { it != null } - return when { - first == null -> null - first.isLeaf -> first //fast search for first entry if it is value - else -> Laminate(filterNotNull()) //merge nodes if first encountered node is meta - } -} \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/visionDescriptor.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/visionDescriptor.kt index a02a3b3e..15ef9229 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/visionDescriptor.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/visionDescriptor.kt @@ -7,20 +7,20 @@ import space.kscience.dataforge.values.asValue private const val INHERITED_DESCRIPTOR_ATTRIBUTE = "inherited" private const val STYLE_DESCRIPTOR_ATTRIBUTE = "useStyles" -public val MetaDescriptor.inherited: Boolean - get() = attributes[INHERITED_DESCRIPTOR_ATTRIBUTE].boolean ?: false +public val MetaDescriptor.inherited: Boolean? + get() = attributes[INHERITED_DESCRIPTOR_ATTRIBUTE].boolean -public var MetaDescriptorBuilder.inherited: Boolean - get() = attributes[INHERITED_DESCRIPTOR_ATTRIBUTE].boolean ?: false - set(value) = attributes.set(INHERITED_DESCRIPTOR_ATTRIBUTE, value) +public var MetaDescriptorBuilder.inherited: Boolean? + get() = attributes[INHERITED_DESCRIPTOR_ATTRIBUTE].boolean + set(value) = attributes.set(INHERITED_DESCRIPTOR_ATTRIBUTE, value?.asValue()) -public val MetaDescriptor.usesStyles: Boolean - get() = attributes[STYLE_DESCRIPTOR_ATTRIBUTE].boolean ?: true +public val MetaDescriptor.usesStyles: Boolean? + get() = attributes[STYLE_DESCRIPTOR_ATTRIBUTE].boolean -public var MetaDescriptorBuilder.usesStyles: Boolean - get() = attributes[STYLE_DESCRIPTOR_ATTRIBUTE].boolean ?: true - set(value) = attributes.set(STYLE_DESCRIPTOR_ATTRIBUTE, value) +public var MetaDescriptorBuilder.usesStyles: Boolean? + get() = attributes[STYLE_DESCRIPTOR_ATTRIBUTE].boolean + set(value) = attributes.set(STYLE_DESCRIPTOR_ATTRIBUTE, value?.asValue()) public val MetaDescriptor.widget: Meta get() = attributes["widget"] ?: Meta.EMPTY @@ -38,7 +38,7 @@ public val MetaDescriptor.widgetType: String? get() = attributes["widget.type"].string /** - * Extension property to access the "widget.type" key of [ValueDescriptor] + * Extension property to access the "widget.type" key of [MetaDescriptorBuilder] */ public var MetaDescriptorBuilder.widgetType: String? get() = attributes["widget.type"].string diff --git a/visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/html/HtmlTagTest.kt b/visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/html/HtmlTagTest.kt index c09c7c6c..3572dd42 100644 --- a/visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/html/HtmlTagTest.kt +++ b/visionforge-core/src/commonTest/kotlin/space/kscience/visionforge/html/HtmlTagTest.kt @@ -12,9 +12,6 @@ import space.kscience.dataforge.names.Name import space.kscience.visionforge.Vision import space.kscience.visionforge.VisionBase import space.kscience.visionforge.VisionManager -import kotlin.collections.HashMap -import kotlin.collections.Map -import kotlin.collections.forEach import kotlin.collections.set import kotlin.test.Test diff --git a/visionforge-fx/build.gradle.kts b/visionforge-fx/build.gradle.kts index 09adb66a..961d181d 100644 --- a/visionforge-fx/build.gradle.kts +++ b/visionforge-fx/build.gradle.kts @@ -14,17 +14,10 @@ dependencies { api("no.tornado:tornadofx:1.7.20") - api("de.jensd:fontawesomefx-fontawesome:4.7.0-11") { - exclude(group = "org.openjfx") - } - - api("de.jensd:fontawesomefx-commons:11.0") { - exclude(group = "org.openjfx") - } - api("org.fxyz3d:fxyz3d:0.5.4") { exclude(module = "slf4j-simple") } + api("org.jetbrains.kotlinx:kotlinx-coroutines-javafx:${ru.mipt.npm.gradle.KScienceVersions.coroutinesVersion}") implementation("eu.mihosoft.vrl.jcsg:jcsg:0.5.7") { diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/FXMetaModel.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/FXMetaModel.kt index 476890a6..7d8e71b2 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/FXMetaModel.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/FXMetaModel.kt @@ -21,23 +21,29 @@ import tornadofx.* public class FXMetaModel( public val root: M, public val rootDescriptor: MetaDescriptor?, - public val nodeName: Name, - public val title: String = nodeName.lastOrNull()?.toString() ?: "Meta" + public val defaultRoot: Meta?, + public val pathName: Name, + public val title: String = pathName.lastOrNull()?.toString() ?: "Meta" ) : Comparable> { private val existingNode = object: ObjectBinding() { - override fun computeValue(): Meta? =root[nodeName] + override fun computeValue(): Meta? = root[pathName] } + private val defaultNode: Meta? get() = defaultRoot?.getMeta(pathName) + + public val descriptor: MetaDescriptor? = rootDescriptor?.get(pathName) + public val children: ListBinding> = object : ListBinding>() { override fun computeValue(): ObservableList> { val nodeKeys = existingNode.get()?.items?.keys?: emptySet() - val descriptorKeys = descriptor?.children?.keys?.map { NameToken(it) } ?: emptySet() - return (nodeKeys + descriptorKeys).map { + val defaultKeys = defaultNode?.items?.keys ?: emptySet() + return (nodeKeys + defaultKeys).map { FXMetaModel( root, rootDescriptor, - nodeName + it + defaultRoot, + pathName + it ) }.filter(filter).asObservable() } @@ -47,16 +53,14 @@ public class FXMetaModel( //add listener to the root node if possible if (root is ObservableMeta) { root.onChange(this) { changed -> - if (changed.startsWith(nodeName)) { - if (nodeName.length == changed.length) existingNode.invalidate() - else if (changed.length == nodeName.length + 1) children.invalidate() + if (changed.startsWith(pathName)) { + if (pathName.length == changed.length) existingNode.invalidate() + else if (changed.length == pathName.length + 1) children.invalidate() } } } } - public val descriptor: MetaDescriptor? = rootDescriptor?.get(nodeName) - public val existsProperty: BooleanBinding = existingNode.isNotNull public val exists: Boolean by existsProperty @@ -66,7 +70,7 @@ public class FXMetaModel( } override fun compareTo(other: FXMetaModel<*>): Int = if (this.exists == other.exists) { - this.nodeName.toString().compareTo(other.nodeName.toString()) + this.pathName.toString().compareTo(other.pathName.toString()) } else { this.exists.compareTo(other.exists) } @@ -79,7 +83,8 @@ public class FXMetaModel( public fun root( node: M, descriptor: MetaDescriptor? = null, + defaultRoot: Meta? = null, rootName: String = "root" - ): FXMetaModel = FXMetaModel(node, descriptor, Name.EMPTY, title = rootName) + ): FXMetaModel = FXMetaModel(node, descriptor, defaultRoot, Name.EMPTY, title = rootName) } } \ No newline at end of file diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/MetaViewer.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/MetaViewer.kt index 92c1222f..4563ade5 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/MetaViewer.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/MetaViewer.kt @@ -26,14 +26,12 @@ import space.kscience.visionforge.dfIconView import tornadofx.* public class MetaViewer( - private val rootNode: FXMetaModel, + private val rootNode: FXMetaModel, title: String = "Meta viewer" ) : Fragment(title, dfIconView) { public constructor(meta: Meta, title: String = "Meta viewer") : this( - FXMetaModel.root( - meta - ), title = title + FXMetaModel.root(meta), title = title ) override val root: BorderPane = borderpane { diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/MutableMetaEditor.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/MutableMetaEditor.kt index 59f49c38..231029cb 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/MutableMetaEditor.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/MutableMetaEditor.kt @@ -13,7 +13,6 @@ import javafx.scene.paint.Color import javafx.scene.text.Text import space.kscience.dataforge.context.Global import space.kscience.dataforge.meta.MutableMeta -import space.kscience.dataforge.meta.descriptors.MetaDescriptor import space.kscience.dataforge.meta.remove import space.kscience.visionforge.dfIconView import tornadofx.* @@ -25,16 +24,16 @@ import tornadofx.* */ public class MutableMetaEditor( public val rootNode: FXMetaModel, - public val allowNew: Boolean = true, - title: String = "Configuration editor" + //public val allowNew: Boolean = true, + title: String = "Meta editor" ) : Fragment(title = title, icon = dfIconView) { //TODO replace parameters by properties - - public constructor( - MutableMeta: MutableMeta, - descriptor: MetaDescriptor?, - title: String = "Configuration editor" - ) : this(FXMetaModel.root(MutableMeta, descriptor = descriptor), title = title) +// +// public constructor( +// MutableMeta: MutableMeta, +// descriptor: MetaDescriptor?, +// title: String = "Configuration editor" +// ) : this(FXMetaModel.root(MutableMeta, descriptor = descriptor), title = title) override val root: BorderPane = borderpane { center = treetableview> { @@ -65,7 +64,7 @@ public class MutableMetaEditor( contextmenu { item("Remove") { action { - content.root.remove(content.nodeName) + content.root.remove(content.pathName) } } } @@ -128,7 +127,7 @@ public class MutableMetaEditor( item.valueProperty, item.descriptor ) { value -> - item.root.setValue(item.nodeName, value) + item.root.setValue(item.pathName, value) } graphic = chooser.node diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/VisionEditorFragment.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/VisionEditorFragment.kt index d159dd11..bf1033ba 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/VisionEditorFragment.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/VisionEditorFragment.kt @@ -5,31 +5,40 @@ import javafx.beans.property.SimpleObjectProperty import javafx.scene.Node import javafx.scene.Parent import javafx.scene.layout.VBox +import space.kscience.dataforge.meta.MutableMeta import space.kscience.dataforge.meta.ObservableMutableMeta import space.kscience.dataforge.meta.descriptors.MetaDescriptor +import space.kscience.dataforge.names.Name import space.kscience.visionforge.Vision import space.kscience.visionforge.computeProperties import space.kscience.visionforge.getStyle import space.kscience.visionforge.styles import tornadofx.* -public class VisionEditorFragment(public val selector: (Vision) -> ObservableMutableMeta = {it.computeProperties()}) : Fragment() { +public class VisionEditorFragment : Fragment() { public val visionProperty: SimpleObjectProperty = SimpleObjectProperty() public var vision: Vision? by visionProperty public val descriptorProperty: SimpleObjectProperty = SimpleObjectProperty() private val configProperty: Binding = visionProperty.objectBinding { vision -> - vision?.let(selector) + vision?.meta } private val configEditorProperty: Binding = configProperty.objectBinding(descriptorProperty) { - it?.let { - MutableMetaEditor(it, descriptorProperty.get()).root + it?.let { meta -> + val node:FXMetaModel = FXMetaModel( + meta, + vision?.descriptor, + vision?.computeProperties(), + Name.EMPTY, + "Vision properties" + ) + MutableMetaEditor(node).root } } - private val styleBoxProperty: Binding = configProperty.objectBinding() { + private val styleBoxProperty: Binding = configProperty.objectBinding { VBox().apply { vision?.styles?.forEach { styleName -> val styleMeta = vision?.getStyle(styleName) diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FX3DPlugin.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FX3DPlugin.kt index 72a7ac8a..84dcb31d 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FX3DPlugin.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FX3DPlugin.kt @@ -24,7 +24,7 @@ import kotlin.collections.set import kotlin.math.PI import kotlin.reflect.KClass -class FX3DPlugin : AbstractPlugin() { +public class FX3DPlugin : AbstractPlugin() { override val tag: PluginTag get() = Companion.tag private val objectFactories = HashMap, FX3DFactory<*>>() @@ -43,7 +43,7 @@ class FX3DPlugin : AbstractPlugin() { as FX3DFactory? } - fun buildNode(obj: Solid): Node { + public fun buildNode(obj: Solid): Node { val binding = VisualObjectFXBinding(this, obj) return when (obj) { is SolidReferenceGroup -> referenceFactory(obj, binding) diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXMaterials.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXMaterials.kt index 0b205626..ca5e6583 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXMaterials.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXMaterials.kt @@ -37,24 +37,22 @@ public object FXMaterials { * Infer color based on meta item * @param opacity default opacity */ -public fun Meta.color(opacity: Double = 1.0): Color { - return value?.let { - if (it.type == ValueType.NUMBER) { - val int = it.int - val red = int and 0x00ff0000 shr 16 - val green = int and 0x0000ff00 shr 8 - val blue = int and 0x000000ff - Color.rgb(red, green, blue, opacity) - } else { - Color.web(it.string) - } - } ?: Color.rgb( - this[Colors.RED_KEY]?.int ?: 0, - this[Colors.GREEN_KEY]?.int ?: 0, - this[Colors.BLUE_KEY]?.int ?: 0, - this[SolidMaterial.OPACITY_KEY]?.double ?: opacity - ) -} +public fun Meta.color(opacity: Double = 1.0): Color = value?.let { + if (it.type == ValueType.NUMBER) { + val int = it.int + val red = int and 0x00ff0000 shr 16 + val green = int and 0x0000ff00 shr 8 + val blue = int and 0x000000ff + Color.rgb(red, green, blue, opacity) + } else { + Color.web(it.string) + } +} ?: Color.rgb( + this[Colors.RED_KEY]?.int ?: 0, + this[Colors.GREEN_KEY]?.int ?: 0, + this[Colors.BLUE_KEY]?.int ?: 0, + this[SolidMaterial.OPACITY_KEY]?.double ?: opacity +) /** * Infer FX material based on meta item diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXReferenceFactory.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXReferenceFactory.kt index ac05e8d5..af00f7c5 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXReferenceFactory.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FXReferenceFactory.kt @@ -10,7 +10,7 @@ import space.kscience.visionforge.Vision import space.kscience.visionforge.onPropertyChange import kotlin.reflect.KClass -class FXReferenceFactory(val plugin: FX3DPlugin) : FX3DFactory { +public class FXReferenceFactory(public val plugin: FX3DPlugin) : FX3DFactory { override val type: KClass get() = SolidReferenceGroup::class override fun invoke(obj: SolidReferenceGroup, binding: VisualObjectFXBinding): Node { diff --git a/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/GdmlTransformer.kt b/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/GdmlTransformer.kt index 0a837919..269df511 100644 --- a/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/GdmlTransformer.kt +++ b/visionforge-gdml/src/commonMain/kotlin/space/kscience/visionforge/gdml/GdmlTransformer.kt @@ -441,20 +441,6 @@ private class GdmlTransformerEnv(val settings: GdmlTransformer) { } final.useStyle(rootStyle) - //inline prototypes -// referenceStore.forEach { (protoName, list) -> -// val proxy = list.singleOrNull() ?: return@forEach -// val parent = proxy.parent as? MutableVisionGroup ?: return@forEach -// val token = parent.children.entries.find { it.value == proxy }?.key ?: error("Inconsistent reference cache") -// val prototype = proto[protoName] as? Solid ?: error("Inconsistent reference cache") -// prototype.parent = null -// parent[token] = prototype -// prototype.updateFrom(proxy) -// -// //FIXME update prototype -// proto[protoName] = null -// } - final.prototypes { proto.children.forEach { (token, item) -> item.parent = null diff --git a/visionforge-plotly/src/commonMain/kotlin/space/kscience/visionforge/plotly/VisionOfPlotly.kt b/visionforge-plotly/src/commonMain/kotlin/space/kscience/visionforge/plotly/VisionOfPlotly.kt index 36102036..8874c694 100644 --- a/visionforge-plotly/src/commonMain/kotlin/space/kscience/visionforge/plotly/VisionOfPlotly.kt +++ b/visionforge-plotly/src/commonMain/kotlin/space/kscience/visionforge/plotly/VisionOfPlotly.kt @@ -15,7 +15,6 @@ public class VisionOfPlotly private constructor() : VisionBase() { public constructor(plot: Plot) : this() { properties = plot.meta } - public val plot: Plot get() = Plot(meta) } diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/ColorAccessor.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/ColorAccessor.kt index 25631c42..f06a7c6f 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/ColorAccessor.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/ColorAccessor.kt @@ -1,29 +1,28 @@ package space.kscience.visionforge.solid -import space.kscience.dataforge.meta.* import space.kscience.dataforge.names.Name +import space.kscience.dataforge.names.plus +import space.kscience.dataforge.values.MutableValueProvider import space.kscience.dataforge.values.Value import space.kscience.dataforge.values.asValue import space.kscience.dataforge.values.string import space.kscience.visionforge.Colors -import space.kscience.visionforge.Vision import space.kscience.visionforge.VisionBuilder -import space.kscience.visionforge.VisionPropertyContainer -import kotlin.jvm.JvmInline @VisionBuilder -public class ColorAccessor(private val colorKey: Name, private val parent: () -> MutableMetaProvider) { +public class ColorAccessor(private val provider: MutableValueProvider, private val colorKey: Name) : + MutableValueProvider { public var value: Value? - get() = parent().getMeta(colorKey)?.value + get() = provider.getValue(colorKey) set(value) { - parent().setValue(colorKey,value) + provider.setValue(colorKey, value) } - public var item: Meta? - get() = parent().getMeta(colorKey) - set(value) { - parent().setMeta(colorKey,value) - } + override fun getValue(name: Name): Value? = provider.getValue(colorKey + name) + + override fun setValue(name: Name, value: Value?) { + provider.setValue(colorKey + name, value) + } } public var ColorAccessor?.string: String? diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Composite.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Composite.kt index b3770d37..9debf612 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Composite.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Composite.kt @@ -2,9 +2,10 @@ package space.kscience.visionforge.solid import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import space.kscience.dataforge.meta.configure import space.kscience.dataforge.meta.update -import space.kscience.visionforge.* +import space.kscience.visionforge.VisionBuilder +import space.kscience.visionforge.VisionContainerBuilder +import space.kscience.visionforge.set public enum class CompositeType { SUM, // Dumb sum of meshes @@ -30,34 +31,38 @@ public inline fun VisionContainerBuilder.composite( val group = SolidGroup().apply(builder) val children = group.children.values.filterIsInstance() if (children.size != 2) error("Composite requires exactly two children") - return Composite(type, children[0], children[1]).apply { - configure { - update(group.meta) - } - if (group.position != null) { - position = group.position - } - if (group.rotation != null) { - rotation = group.rotation - } - if (group.scale != null) { - scale = group.scale - } - set(name, this) + val res = Composite(type, children[0], children[1]) + + res.meta.update(group.meta) + + if (group.position != null) { + res.position = group.position } + if (group.rotation != null) { + res.rotation = group.rotation + } + if (group.scale != null) { + res.scale = group.scale + } + + set(name, res) + return res } @VisionBuilder -public inline fun VisionContainerBuilder.union(name: String? = null, builder: SolidGroup.() -> Unit): Composite = - composite(CompositeType.UNION, name, builder = builder) +public inline fun VisionContainerBuilder.union( + name: String? = null, + builder: SolidGroup.() -> Unit +): Composite = composite(CompositeType.UNION, name, builder = builder) @VisionBuilder -public inline fun VisionContainerBuilder.subtract(name: String? = null, builder: SolidGroup.() -> Unit): Composite = - composite(CompositeType.SUBTRACT, name, builder = builder) +public inline fun VisionContainerBuilder.subtract( + name: String? = null, + builder: SolidGroup.() -> Unit +): Composite = composite(CompositeType.SUBTRACT, name, builder = builder) @VisionBuilder public inline fun VisionContainerBuilder.intersect( name: String? = null, builder: SolidGroup.() -> Unit, -): Composite = - composite(CompositeType.INTERSECT, name, builder = builder) \ No newline at end of file +): Composite = composite(CompositeType.INTERSECT, name, builder = builder) \ No newline at end of file diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidBase.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidBase.kt index f18a3869..7ca5c45d 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidBase.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidBase.kt @@ -2,6 +2,7 @@ package space.kscience.visionforge.solid import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +import space.kscience.dataforge.meta.MutableMeta import space.kscience.dataforge.meta.descriptors.MetaDescriptor import space.kscience.visionforge.VisionBase import space.kscience.visionforge.VisionChange @@ -9,6 +10,9 @@ import space.kscience.visionforge.VisionChange @Serializable @SerialName("solid") public open class SolidBase : VisionBase(), Solid { + //FIXME to be removed after https://github.com/Kotlin/kotlinx.serialization/issues/1602 fix + override var properties: MutableMeta? = null + override val descriptor: MetaDescriptor get() = Solid.descriptor override fun update(change: VisionChange) { diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidGroup.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidGroup.kt index f0f28fc5..ec6dd5f1 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidGroup.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidGroup.kt @@ -2,6 +2,7 @@ package space.kscience.visionforge.solid import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +import space.kscience.dataforge.meta.MutableMeta import space.kscience.dataforge.meta.descriptors.MetaDescriptor import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.NameToken @@ -31,6 +32,9 @@ public interface PrototypeHolder { @SerialName("group.solid") public class SolidGroup : VisionGroupBase(), Solid, PrototypeHolder { + //FIXME to be removed after https://github.com/Kotlin/kotlinx.serialization/issues/1602 fix + override var properties: MutableMeta? = null + override val children: Map get() = super.childrenInternal.filter { it.key != PROTOTYPES_TOKEN } private var prototypes: MutableVisionGroup? diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidMaterial.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidMaterial.kt index 78d787a3..fa5b0d76 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidMaterial.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidMaterial.kt @@ -20,14 +20,14 @@ public class SolidMaterial : Scheme() { /** * Primary web-color for the material */ - public val color: ColorAccessor = ColorAccessor(COLOR_KEY) { meta } + public val color: ColorAccessor = ColorAccessor(meta, COLOR_KEY) /** * Specular color for phong material */ - public val specularColor: ColorAccessor = ColorAccessor(SPECULAR_COLOR_KEY) { meta } + public val specularColor: ColorAccessor = ColorAccessor(meta, SPECULAR_COLOR_KEY) - public val emissiveColor: ColorAccessor = ColorAccessor("emissiveColor".asName()) { meta } + public val emissiveColor: ColorAccessor = ColorAccessor(meta, "emissiveColor".asName()) /** * Opacity @@ -55,33 +55,29 @@ public class SolidMaterial : Scheme() { //must be lazy to avoid initialization bug MetaDescriptor { inherited = true - usesStyles = true value(COLOR_KEY, ValueType.STRING, ValueType.NUMBER) { inherited = true - usesStyles = true widgetType = "color" } value(SPECULAR_COLOR_KEY, ValueType.STRING, ValueType.NUMBER) { inherited = true - usesStyles = true widgetType = "color" hide() } value(OPACITY_KEY, ValueType.NUMBER) { inherited = true - usesStyles = true default(1.0) attributes["min"] = 0.0 attributes["max"] = 1.0 attributes["step"] = 0.1 widgetType = "slider" } + value(WIREFRAME_KEY, ValueType.BOOLEAN) { inherited = true - usesStyles = true default(false) } } @@ -90,7 +86,7 @@ public class SolidMaterial : Scheme() { } public val Solid.color: ColorAccessor - get() = ColorAccessor(MATERIAL_COLOR_KEY) { computeProperties() } + get() = ColorAccessor(computePropertyValues(), MATERIAL_COLOR_KEY) public var Solid.material: SolidMaterial? get() = computePropertyNode(MATERIAL_KEY)?.let { SolidMaterial.read(it) } diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidReference.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidReference.kt index 427ef9a2..d2080924 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidReference.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidReference.kt @@ -2,6 +2,7 @@ package space.kscience.visionforge.solid import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +import space.kscience.dataforge.meta.MutableMeta import space.kscience.dataforge.meta.ObservableMutableMeta import space.kscience.dataforge.meta.descriptors.MetaDescriptor import space.kscience.dataforge.meta.get @@ -12,11 +13,16 @@ import space.kscience.visionforge.* public interface SolidReference : VisionGroup { /** - * The prototype for this reference. Always returns a "real" prototype, not a reference + * The prototype for this reference. */ public val prototype: Solid - override fun getPropertyValue(name: Name, inherit: Boolean, includeStyles: Boolean, includeDefaults: Boolean): Value? { + override fun getPropertyValue( + name: Name, + inherit: Boolean, + includeStyles: Boolean, + includeDefaults: Boolean + ): Value? { meta[name]?.value?.let { return it } if (includeStyles) { getStyleProperty(name)?.let { return it } @@ -56,6 +62,8 @@ public class SolidReferenceGroup( public val refName: Name, ) : VisionBase(), SolidReference, VisionGroup, Solid { + override var properties: MutableMeta? = null + /** * Recursively search for defined template in the parent */ @@ -72,8 +80,12 @@ public class SolidReferenceGroup( ReferenceChild(this, it.key.asName()) } ?: emptyMap() - override fun getPropertyValue(name: Name, inherit: Boolean, includeStyles: Boolean, includeDefaults: Boolean): Value? = - super.getPropertyValue(name, inherit, includeStyles, includeDefaults) + override fun getPropertyValue( + name: Name, + inherit: Boolean, + includeStyles: Boolean, + includeDefaults: Boolean + ): Value? = super.getPropertyValue(name, inherit, includeStyles, includeDefaults) override val descriptor: MetaDescriptor get() = prototype.descriptor @@ -88,11 +100,14 @@ public class SolidReferenceGroup( ) : SolidReference, VisionGroup, Solid { override val prototype: Solid by lazy { - if (refName.isEmpty()) owner.prototype else { + if (refName.isEmpty()) { + owner.prototype + } else { val proto = (owner.prototype as? VisionGroup)?.get(refName) ?: error("Prototype with name $refName not found in SolidReferenceGroup ${owner.refName}") - proto.unref as? Solid - ?: error("Prototype with name $refName is ${proto::class} but expected Solid") + proto as? Solid ?: error("Prototype with name $refName is ${proto::class} but expected Solid") +// proto.unref as? Solid +// ?: error("Prototype with name $refName is ${proto::class} but expected Solid") } } diff --git a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/PropertyTest.kt b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/PropertyTest.kt index d146195a..ec58e4d2 100644 --- a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/PropertyTest.kt +++ b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/PropertyTest.kt @@ -1,6 +1,7 @@ package space.kscience.visionforge.solid -import space.kscience.dataforge.meta.* +import space.kscience.dataforge.meta.get +import space.kscience.dataforge.meta.string import space.kscience.dataforge.names.asName import space.kscience.dataforge.values.int import space.kscience.visionforge.* @@ -10,13 +11,32 @@ import kotlin.test.assertEquals @Suppress("UNUSED_VARIABLE") class PropertyTest { @Test - fun testColorUpdate(){ + fun testColor(){ val box = Box(10.0f, 10.0f,10.0f) box.material { //meta["color"] = "pink" color("pink") } assertEquals("pink", box.meta["material.color"]?.string) + assertEquals("pink", box.color.string) + } + + @Test + fun testColorUpdate(){ + val box = Box(10.0f, 10.0f,10.0f) + + var c: String? = null + box.onPropertyChange { + if(it == SolidMaterial.MATERIAL_COLOR_KEY){ + c = box.color.string + } + } + + box.material { + color("pink") + } + + assertEquals("pink", c) } @Test diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/MeshThreeFactory.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/MeshThreeFactory.kt index 992035a6..8b1428ec 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/MeshThreeFactory.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/MeshThreeFactory.kt @@ -4,17 +4,20 @@ import info.laht.threekt.core.BufferGeometry import info.laht.threekt.geometries.EdgesGeometry import info.laht.threekt.objects.LineSegments import info.laht.threekt.objects.Mesh -import space.kscience.dataforge.meta.get +import space.kscience.dataforge.meta.updateWith import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.plus import space.kscience.dataforge.names.startsWith import space.kscience.dataforge.values.boolean -import space.kscience.visionforge.computeProperties +import space.kscience.visionforge.computePropertyNode import space.kscience.visionforge.onPropertyChange +import space.kscience.visionforge.setProperty import space.kscience.visionforge.solid.Solid import space.kscience.visionforge.solid.SolidMaterial import space.kscience.visionforge.solid.layer +import space.kscience.visionforge.solid.three.MeshThreeFactory.Companion.EDGES_ENABLED_KEY +import space.kscience.visionforge.solid.three.MeshThreeFactory.Companion.EDGES_MATERIAL_KEY import kotlin.reflect.KClass /** @@ -62,6 +65,7 @@ public abstract class MeshThreeFactory( public companion object { public val EDGES_KEY: Name = "edges".asName() + //public val WIREFRAME_KEY: Name = "wireframe".asName() public val ENABLED_KEY: Name = "enabled".asName() public val EDGES_ENABLED_KEY: Name = EDGES_KEY + ENABLED_KEY @@ -71,6 +75,11 @@ public abstract class MeshThreeFactory( } } +public fun Solid.edges(enabled: Boolean = true, block: SolidMaterial.() -> Unit = {}) { + setProperty(EDGES_ENABLED_KEY, enabled) + meta.getOrCreate(EDGES_MATERIAL_KEY).updateWith(SolidMaterial, block) +} + internal fun Mesh.applyProperties(obj: Solid): Mesh = apply { updateMaterial(obj) applyEdges(obj) @@ -84,12 +93,9 @@ internal fun Mesh.applyProperties(obj: Solid): Mesh = apply { public fun Mesh.applyEdges(obj: Solid) { val edges = children.find { it.name == "@edges" } as? LineSegments //inherited edges definition, enabled by default - if (obj.getPropertyValue(MeshThreeFactory.EDGES_ENABLED_KEY, inherit = true, includeStyles = true)?.boolean != false) { + if (obj.getPropertyValue(EDGES_ENABLED_KEY, inherit = true)?.boolean != false) { val bufferGeometry = geometry as? BufferGeometry ?: return - val material = ThreeMaterials.getLineMaterial( - obj.computeProperties().get(MeshThreeFactory.EDGES_MATERIAL_KEY), - true - ) + val material = ThreeMaterials.getLineMaterial(obj.computePropertyNode(EDGES_MATERIAL_KEY), true) if (edges == null) { add( LineSegments( diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCanvasLabelFactory.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCanvasLabelFactory.kt index 4855abcd..7dd30a34 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCanvasLabelFactory.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCanvasLabelFactory.kt @@ -12,7 +12,7 @@ import org.w3c.dom.CanvasTextBaseline import org.w3c.dom.HTMLCanvasElement import org.w3c.dom.MIDDLE import space.kscience.visionforge.solid.SolidLabel -import space.kscience.visionforge.solid.color +import space.kscience.visionforge.solid.SolidMaterial import space.kscience.visionforge.solid.three.ThreeCanvas.Companion.DO_NOT_HIGHLIGHT_TAG import kotlin.reflect.KClass @@ -26,7 +26,7 @@ public object ThreeCanvasLabelFactory : ThreeFactory { val canvas = document.createElement("canvas") as HTMLCanvasElement val context = canvas.getContext("2d") as CanvasRenderingContext2D context.font = "Bold ${obj.fontSize}pt ${obj.fontFamily}" - context.fillStyle = obj.color.value ?: "black" + context.fillStyle = obj.getPropertyValue(SolidMaterial.MATERIAL_COLOR_KEY)?.value ?: "black" context.textBaseline = CanvasTextBaseline.MIDDLE val metrics = context.measureText(obj.text) //canvas.width = metrics.width.toInt() diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeMaterials.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeMaterials.kt index 9a3c6be9..dc0ab967 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeMaterials.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeMaterials.kt @@ -3,16 +3,21 @@ package space.kscience.visionforge.solid.three import info.laht.threekt.materials.LineBasicMaterial import info.laht.threekt.materials.Material import info.laht.threekt.materials.MeshBasicMaterial -import info.laht.threekt.materials.MeshPhongMaterial import info.laht.threekt.math.Color import info.laht.threekt.objects.Mesh -import space.kscience.dataforge.meta.* +import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.boolean +import space.kscience.dataforge.meta.double +import space.kscience.dataforge.meta.get import space.kscience.dataforge.names.Name +import space.kscience.dataforge.names.asName import space.kscience.dataforge.values.* import space.kscience.visionforge.Colors import space.kscience.visionforge.Vision import space.kscience.visionforge.computePropertyNode +import space.kscience.visionforge.getStyleNodes import space.kscience.visionforge.solid.SolidMaterial +import space.kscience.visionforge.solid.SolidReference public object ThreeMaterials { @@ -56,56 +61,57 @@ public object ThreeMaterials { private val materialCache = HashMap() - internal fun buildMaterial(meta: Meta): Material { - val material = SolidMaterial.read(meta) - return meta[SolidMaterial.SPECULAR_COLOR_KEY]?.let { specularColor -> - MeshPhongMaterial().apply { - color = meta[SolidMaterial.COLOR_KEY]?.threeColor() ?: DEFAULT_COLOR - specular = specularColor.threeColor() - emissive = material.emissiveColor.item?.threeColor() ?: specular - reflectivity = 0.5 - refractionRatio = 1.0 - shininess = 100.0 - opacity = meta[SolidMaterial.OPACITY_KEY]?.double ?: 1.0 - transparent = opacity < 1.0 - wireframe = meta[SolidMaterial.WIREFRAME_KEY].boolean ?: false - needsUpdate = true - } - } ?: MeshBasicMaterial().apply { - color = meta[SolidMaterial.COLOR_KEY]?.threeColor() ?: DEFAULT_COLOR - opacity = meta[SolidMaterial.OPACITY_KEY]?.double ?: 1.0 - transparent = opacity < 1.0 - wireframe = meta[SolidMaterial.WIREFRAME_KEY].boolean ?: false - needsUpdate = true - } - + internal fun buildMaterial(meta: Meta): Material = MeshBasicMaterial().apply { + color = meta[SolidMaterial.COLOR_KEY]?.threeColor() ?: DEFAULT_COLOR + opacity = meta[SolidMaterial.OPACITY_KEY]?.double ?: 1.0 + transparent = opacity < 1.0 + wireframe = meta[SolidMaterial.WIREFRAME_KEY].boolean ?: false + needsUpdate = true } +// val material = SolidMaterial.read(meta) +// return meta[SolidMaterial.SPECULAR_COLOR_KEY]?.let { specularColor -> +// MeshPhongMaterial().apply { +// color = meta[SolidMaterial.COLOR_KEY]?.threeColor() ?: DEFAULT_COLOR +// specular = specularColor.threeColor() +// emissive = material.emissiveColor.threeColor() ?: specular +// reflectivity = 0.5 +// refractionRatio = 1.0 +// shininess = 100.0 +// opacity = meta[SolidMaterial.OPACITY_KEY]?.double ?: 1.0 +// transparent = opacity < 1.0 +// wireframe = meta[SolidMaterial.WIREFRAME_KEY].boolean ?: false +// needsUpdate = true +// } +// } ?: MeshBasicMaterial().apply { +// color = meta[SolidMaterial.COLOR_KEY]?.threeColor() ?: DEFAULT_COLOR +// opacity = meta[SolidMaterial.OPACITY_KEY]?.double ?: 1.0 +// transparent = opacity < 1.0 +// wireframe = meta[SolidMaterial.WIREFRAME_KEY].boolean ?: false +// needsUpdate = true +// } internal fun cacheMaterial(meta: Meta): Material = materialCache.getOrPut(meta) { buildMaterial(meta).apply { cached = true } } - } /** - * Infer color based on meta item + * Compute color */ -public fun Meta.threeColor(): Color { - return value?.let { value -> - if (value.type == ValueType.NUMBER) { - val int = value.int - Color(int) - } else { - Color(value.string) - } - } ?: Color( - this[Colors.RED_KEY]?.int ?: 0, - this[Colors.GREEN_KEY]?.int ?: 0, - this[Colors.BLUE_KEY]?.int ?: 0 - ) -} +public fun Meta.threeColor(): Color = getValue(Name.EMPTY)?.let { value -> + if (value.type == ValueType.NUMBER) { + val int = value.int + Color(int) + } else { + Color(value.string) + } +} ?: Color( + getValue(Colors.RED_KEY.asName())?.int ?: 0, + getValue(Colors.GREEN_KEY.asName())?.int ?: 0, + getValue(Colors.BLUE_KEY.asName())?.int ?: 0 +) private var Material.cached: Boolean get() = userData["cached"] == true @@ -114,31 +120,19 @@ private var Material.cached: Boolean } public fun Mesh.updateMaterial(vision: Vision) { - //val meta = vision.getProperty(SolidMaterial.MATERIAL_KEY, inherit = true).node val ownMaterialMeta = vision.meta.getMeta(SolidMaterial.MATERIAL_KEY) - val parentMaterialMeta = vision.parent?.getPropertyValue( - SolidMaterial.MATERIAL_KEY, - inherit = true, - includeStyles = false, - includeDefaults = false - ) - - material = when { - ownMaterialMeta == null && parentMaterialMeta == null -> { - //If material is style-based, use cached - vision.computePropertyNode( - SolidMaterial.MATERIAL_KEY, - )?.let { + if (ownMaterialMeta == null) { + if (vision is SolidReference && vision.getStyleNodes(SolidMaterial.MATERIAL_KEY).isEmpty()) { + updateMaterial(vision.prototype) + } else { + material = vision.computePropertyNode(SolidMaterial.MATERIAL_KEY)?.let { ThreeMaterials.cacheMaterial(it) } ?: ThreeMaterials.DEFAULT } - else -> { - vision.computePropertyNode( - SolidMaterial.MATERIAL_KEY, - )?.let { - ThreeMaterials.buildMaterial(it) - } ?: ThreeMaterials.DEFAULT - } + } else { + material = vision.computePropertyNode(SolidMaterial.MATERIAL_KEY)?.let { + ThreeMaterials.buildMaterial(it) + } ?: ThreeMaterials.DEFAULT } } @@ -149,17 +143,14 @@ public fun Mesh.updateMaterialProperty(vision: Vision, propertyName: Name) { } else { when (propertyName) { SolidMaterial.MATERIAL_COLOR_KEY -> { - material.asDynamic().color = vision.computePropertyNode( - SolidMaterial.MATERIAL_COLOR_KEY, - )?.threeColor() ?: ThreeMaterials.DEFAULT_COLOR + material.asDynamic().color = vision.computePropertyNode(SolidMaterial.MATERIAL_COLOR_KEY)?.threeColor() + ?: ThreeMaterials.DEFAULT_COLOR material.needsUpdate = true } SolidMaterial.MATERIAL_OPACITY_KEY -> { val opacity = vision.getPropertyValue( SolidMaterial.MATERIAL_OPACITY_KEY, inherit = true, - includeStyles = true, - includeDefaults = false )?.double ?: 1.0 material.opacity = opacity material.transparent = opacity < 1.0 @@ -169,8 +160,6 @@ public fun Mesh.updateMaterialProperty(vision: Vision, propertyName: Name) { material.asDynamic().wireframe = vision.getPropertyValue( SolidMaterial.MATERIAL_WIREFRAME_KEY, inherit = true, - includeStyles = true, - includeDefaults = false )?.boolean ?: false material.needsUpdate = true } diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeReferenceFactory.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeReferenceFactory.kt index 53e9c2c9..aa779e46 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeReferenceFactory.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeReferenceFactory.kt @@ -1,6 +1,5 @@ package space.kscience.visionforge.solid.three -import info.laht.threekt.core.BufferGeometry import info.laht.threekt.core.Object3D import info.laht.threekt.objects.Mesh import space.kscience.dataforge.names.Name @@ -19,7 +18,7 @@ public object ThreeReferenceFactory : ThreeFactory { private fun Object3D.replicate(): Object3D { return when (this) { - is Mesh -> Mesh(geometry as BufferGeometry, material).also { + is Mesh -> Mesh(geometry, material).also { it.applyMatrix4(matrix) } else -> clone(false) From e38bac8d230ba55324a4faf7ee94c2e749638959 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Wed, 11 Aug 2021 18:17:47 +0300 Subject: [PATCH 06/15] Fix MM model --- .../kotlin/ru/mipt/npm/muon/monitor/Model.kt | 4 +++- .../kotlin/ru/mipt/npm/muon/monitor/Monitor.kt | 3 ++- .../ru/mipt/npm/muon/monitor/MMAppComponent.kt | 6 ++++-- .../kotlin/ru/mipt/npm/muon/monitor/sim/Pixel.kt | 12 ++++++------ .../visionforge/solid/three/ThreeLineFactory.kt | 10 +++++++--- 5 files changed, 22 insertions(+), 13 deletions(-) diff --git a/demo/muon-monitor/src/commonMain/kotlin/ru/mipt/npm/muon/monitor/Model.kt b/demo/muon-monitor/src/commonMain/kotlin/ru/mipt/npm/muon/monitor/Model.kt index 854e520c..0c5e0af0 100644 --- a/demo/muon-monitor/src/commonMain/kotlin/ru/mipt/npm/muon/monitor/Model.kt +++ b/demo/muon-monitor/src/commonMain/kotlin/ru/mipt/npm/muon/monitor/Model.kt @@ -28,7 +28,6 @@ class Model(val manager: VisionManager) { private fun SolidGroup.detector(detector: SC16) { group(detector.name) { - position = detector.center detector.pixels.forEach { pixel(it) } @@ -65,6 +64,7 @@ class Model(val manager: VisionManager) { } private fun highlight(pixel: String) { + println("highlight $pixel") map[pixel]?.color?.invoke("blue") } @@ -76,6 +76,7 @@ class Model(val manager: VisionManager) { } fun displayEvent(event: Event) { + println("Received event: $event") events.add(event) event.hits.forEach { highlight(it) @@ -83,6 +84,7 @@ class Model(val manager: VisionManager) { event.track?.let { tracks.polyline(*it.toTypedArray(), name = "track[${event.id}]") { thickness = 4 + color("red") } } } diff --git a/demo/muon-monitor/src/commonMain/kotlin/ru/mipt/npm/muon/monitor/Monitor.kt b/demo/muon-monitor/src/commonMain/kotlin/ru/mipt/npm/muon/monitor/Monitor.kt index 3b64bb5a..3e1db5bc 100644 --- a/demo/muon-monitor/src/commonMain/kotlin/ru/mipt/npm/muon/monitor/Monitor.kt +++ b/demo/muon-monitor/src/commonMain/kotlin/ru/mipt/npm/muon/monitor/Monitor.kt @@ -3,6 +3,7 @@ package ru.mipt.npm.muon.monitor import ru.mipt.npm.muon.monitor.Monitor.PIXEL_XY_SIZE import ru.mipt.npm.muon.monitor.Monitor.PIXEL_Z_SIZE import space.kscience.visionforge.solid.Point3D +import space.kscience.visionforge.solid.plus /** * A single pixel @@ -97,7 +98,7 @@ class SC16( } val offset = Point3D(-y, x, 0)//rotateDetector(Point3D(x, y, 0.0)); val pixelName = "${name}_${index}" - SC1(pixelName, offset) + SC1(pixelName, offset + center) } } } diff --git a/demo/muon-monitor/src/jsMain/kotlin/ru/mipt/npm/muon/monitor/MMAppComponent.kt b/demo/muon-monitor/src/jsMain/kotlin/ru/mipt/npm/muon/monitor/MMAppComponent.kt index d981fa44..8450cb4a 100644 --- a/demo/muon-monitor/src/jsMain/kotlin/ru/mipt/npm/muon/monitor/MMAppComponent.kt +++ b/demo/muon-monitor/src/jsMain/kotlin/ru/mipt/npm/muon/monitor/MMAppComponent.kt @@ -56,8 +56,10 @@ val MMApp = functionalComponent("Muon monitor") { props -> } } - val root = props.model.root.apply { - edges() + val root = useMemo(props.model) { + props.model.root.apply { + edges() + } } gridRow { diff --git a/demo/muon-monitor/src/jvmMain/kotlin/ru/mipt/npm/muon/monitor/sim/Pixel.kt b/demo/muon-monitor/src/jvmMain/kotlin/ru/mipt/npm/muon/monitor/sim/Pixel.kt index 579bca15..7cd54417 100644 --- a/demo/muon-monitor/src/jvmMain/kotlin/ru/mipt/npm/muon/monitor/sim/Pixel.kt +++ b/demo/muon-monitor/src/jvmMain/kotlin/ru/mipt/npm/muon/monitor/sim/Pixel.kt @@ -16,10 +16,10 @@ import kotlin.random.Random */ internal class SC1Aux(val sc: SC1, var efficiency: Double = 1.0) { // val layer: Layer = findLayer(center.z); - private val upLayer = - findLayer(sc.center.z + sc.zSize / 2f)//Layer("${name}_up", center.z + zSize / 2.0); - private val bottomLayer = - findLayer(sc.center.z - sc.zSize / 2f)//Layer("${name}_bottom", center.z - zSize / 2.0); + private val upLayer = findLayer(sc.center.z + sc.zSize / 2f) + //Layer("${name}_up", center.z + zSize / 2.0); + private val bottomLayer = findLayer(sc.center.z - sc.zSize / 2f) + //Layer("${name}_bottom", center.z - zSize / 2.0); private val centralLayer = findLayer(sc.center.z) private val center = Vector3D(sc.center.x.toDouble(), sc.center.y.toDouble(), sc.center.z.toDouble()) @@ -115,8 +115,8 @@ internal class SC1Aux(val sc: SC1, var efficiency: Double = 1.0) { private val auxCache = HashMap() -fun SC1.isHit(track: Line): Boolean{ - return auxCache.getOrPut(this){ +fun SC1.isHit(track: Line): Boolean { + return auxCache.getOrPut(this) { SC1Aux(this) }.isHit(track) } \ No newline at end of file diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeLineFactory.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeLineFactory.kt index 49fca0d9..ee36d74b 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeLineFactory.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeLineFactory.kt @@ -7,9 +7,11 @@ import info.laht.threekt.objects.LineSegments import space.kscience.visionforge.computePropertyNode import space.kscience.visionforge.onPropertyChange import space.kscience.visionforge.solid.PolyLine +import space.kscience.visionforge.solid.SolidMaterial import space.kscience.visionforge.solid.color import space.kscience.visionforge.solid.string import space.kscience.visionforge.solid.three.ThreeMaterials.DEFAULT_LINE_COLOR +import kotlin.math.ceil import kotlin.reflect.KClass public object ThreeLineFactory : ThreeFactory { @@ -17,12 +19,14 @@ public object ThreeLineFactory : ThreeFactory { override fun invoke(three: ThreePlugin, obj: PolyLine): Object3D { val geometry = BufferGeometry().apply { - setFromPoints(Array(obj.points.size) { obj.points[it].toVector() }) + setFromPoints(Array((obj.points.size - 1) * 2) { + obj.points[ceil(it / 2.0).toInt()].toVector() + }) } val material = ThreeMaterials.getLineMaterial( - obj.computePropertyNode(MeshThreeFactory.EDGES_MATERIAL_KEY), - true + obj.computePropertyNode(SolidMaterial.MATERIAL_KEY), + false ) material.linewidth = obj.thickness.toDouble() From c425f3b36f9afd90662a23387e0badd6b9e69b94 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Wed, 11 Aug 2021 20:33:19 +0300 Subject: [PATCH 07/15] Merge branch 'dev' into feature/df-new # Conflicts: # visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/ValueChooser.kt # visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/ValueChooserBase.kt # visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/VisionTreeFragment.kt # visionforge-fx/src/main/kotlin/space/kscience/visionforge/editor/VisualObjectEditorFragment.kt # visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/VisualObjectFXBinding.kt --- .../main/kotlin/space/kscience/visionforge/solid/FX3DPlugin.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FX3DPlugin.kt b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FX3DPlugin.kt index 84dcb31d..9aed5d50 100644 --- a/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FX3DPlugin.kt +++ b/visionforge-fx/src/main/kotlin/space/kscience/visionforge/solid/FX3DPlugin.kt @@ -150,7 +150,7 @@ public interface FX3DFactory { public operator fun invoke(obj: T, binding: VisualObjectFXBinding): Node public companion object { - public const val TYPE = "fx3DFactory" + public const val TYPE: String = "fx3DFactory" } } From c44162671ffd7f0447adc8b2582b17ccf175f3ac Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 13 Aug 2021 20:40:28 +0300 Subject: [PATCH 08/15] Replace structure changes listener by callback. Fix review issues --- .../main/kotlin/ru/mipt/npm/sat/geometry.kt | 1 + .../space/kscience/visionforge/VisionBase.kt | 2 +- .../kscience/visionforge/VisionChange.kt | 9 ++++-- .../space/kscience/visionforge/VisionGroup.kt | 32 +++++++++++++++---- .../kscience/visionforge/VisionGroupBase.kt | 32 +++++++++++++------ .../visionforge/solid/VisionUpdateTest.kt | 24 -------------- .../visionforge/solid/three/ThreePlugin.kt | 13 ++++---- 7 files changed, 63 insertions(+), 50 deletions(-) diff --git a/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/geometry.kt b/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/geometry.kt index c747f89b..546c7b51 100644 --- a/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/geometry.kt +++ b/demo/sat-demo/src/main/kotlin/ru/mipt/npm/sat/geometry.kt @@ -17,6 +17,7 @@ internal fun visionOfSatellite( ySegmentSize: Number = xSegmentSize, fiberDiameter: Number = 1.0, ): SolidGroup = SolidGroup { + color("darkgreen") val transparent by style { this[SolidMaterial.MATERIAL_OPACITY_KEY] = 0.3 } diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionBase.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionBase.kt index 77a682aa..c7d9413c 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionBase.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionBase.kt @@ -23,7 +23,7 @@ internal data class MetaListener( /** * A full base implementation for a [Vision] - * @param properties Object own properties excluding styles and inheritance + * @param parent the parent object for this vision. Could ve set later. Not serialized. */ @Serializable @SerialName("vision") diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionChange.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionChange.kt index 0275934b..c4f18712 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionChange.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionChange.kt @@ -82,6 +82,7 @@ public inline fun VisionChange(manager: VisionManager, block: VisionChangeBuilde VisionChangeBuilder().apply(block).isolate(manager) +@OptIn(DFExperimental::class) private fun CoroutineScope.collectChange( name: Name, source: Vision, @@ -102,11 +103,13 @@ private fun CoroutineScope.collectChange( //Subscribe for structure change if (source is MutableVisionGroup) { - source.structureChanges.onEach { (token, _, after) -> + source.structureChanges.onEach { changedName -> + val after = source[changedName] + val fullName = name + changedName if (after != null) { - collectChange(name + token, after, collector) + collectChange(fullName, after, collector) } - collector()[name + token] = after + collector()[fullName] = after }.launchIn(this) } } diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroup.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroup.kt index 76640bee..12fe243b 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroup.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroup.kt @@ -1,6 +1,11 @@ package space.kscience.visionforge +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.launch +import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.names.* import space.kscience.dataforge.provider.Provider @@ -69,15 +74,30 @@ public interface VisionContainerBuilder { * Mutable version of [VisionGroup] */ public interface MutableVisionGroup : VisionGroup, VisionContainerBuilder { + public fun onStructureChanged(owner: Any?, block: VisionGroup.(Name) -> Unit) - public data class StructureChange(val token: NameToken, val before: Vision?, val after: Vision?) - - /** - * Flow structure changes of this group. Unconsumed changes are discarded - */ - public val structureChanges: Flow + public fun removeStructureListener(owner: Any?) } + +/** + * Flow structure changes of this group. Unconsumed changes are discarded + */ +@OptIn(ExperimentalCoroutinesApi::class) +@DFExperimental +public val MutableVisionGroup.structureChanges: Flow + get() = callbackFlow { + meta.onChange(this) { name -> + launch { + send(name) + } + } + awaitClose { + removeStructureListener(this) + } + } + + public operator fun VisionContainer.get(str: String): V? = get(Name.parse(str)) public operator fun VisionContainerBuilder.set(token: NameToken, child: V?): Unit = diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroupBase.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroupBase.kt index f4d25b08..7280ae1f 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroupBase.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionGroupBase.kt @@ -1,13 +1,12 @@ package space.kscience.visionforge -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.launch 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] @@ -40,16 +39,24 @@ public open class VisionGroupBase( } @Transient - private val _structureChanges: MutableSharedFlow = MutableSharedFlow() + private val structureListeners = HashSet() - override val structureChanges: SharedFlow get() = _structureChanges + @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 */ - private fun childrenChanged(name: NameToken, before: Vision?, after: Vision?) { - (manager?.context?: GlobalScope).launch { - _structureChanges.emit(MutableVisionGroup.StructureChange(name, before, after)) + protected fun childrenChanged(name: Name) { + structureListeners.forEach { + it.callback(this, name) } } @@ -83,7 +90,12 @@ public open class VisionGroupBase( } } if (before != child) { - childrenChanged(token, before, child) + childrenChanged(token.asName()) + if (child is MutableVisionGroup) { + child.onStructureChanged(this) { changedName -> + this@VisionGroupBase.childrenChanged(token + changedName) + } + } } } diff --git a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/VisionUpdateTest.kt b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/VisionUpdateTest.kt index 7a36ab3f..a08085d6 100644 --- a/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/VisionUpdateTest.kt +++ b/visionforge-solid/src/commonTest/kotlin/space/kscience/visionforge/solid/VisionUpdateTest.kt @@ -49,28 +49,4 @@ class VisionUpdateTest { val reconstructed = visionManager.jsonFormat.decodeFromString(VisionChange.serializer(), serialized) assertEquals(change.properties,reconstructed.properties) } - - @Test - fun testDeserialization(){ - val str = """ - { - "propertyChange": { - "layer[4]": { - "material": { - "color": 123 - } - }, - "layer[2]": { - "material": { - } - } - }, - "childrenChange": { - } - } - """.trimIndent() - - val reconstructed = visionManager.jsonFormat.decodeFromString(VisionChange.serializer(), str) - } - } \ No newline at end of file diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreePlugin.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreePlugin.kt index e7c2ac86..aa5c2a14 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreePlugin.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreePlugin.kt @@ -2,8 +2,6 @@ package space.kscience.visionforge.solid.three import info.laht.threekt.core.Object3D import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach import org.w3c.dom.Element import org.w3c.dom.HTMLElement import space.kscience.dataforge.context.* @@ -15,6 +13,7 @@ import space.kscience.visionforge.Vision import space.kscience.visionforge.onPropertyChange import space.kscience.visionforge.solid.* import space.kscience.visionforge.solid.specifications.Canvas3DOptions +import space.kscience.visionforge.solid.three.set import space.kscience.visionforge.visible import kotlin.collections.set import kotlin.reflect.KClass @@ -82,9 +81,11 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer { } } - obj.structureChanges.onEach { (nameToken, _, child) -> + obj.onStructureChanged(this){ childName -> + val child = get(childName) + //removing old object - findChild(nameToken.asName())?.let { oldChild -> + findChild(childName)?.let { oldChild -> oldChild.parent?.remove(oldChild) } @@ -92,12 +93,12 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer { if (child != null && child is Solid) { try { val object3D = buildObject3D(child) - set(nameToken, object3D) + set(childName, object3D) } catch (ex: Throwable) { logger.error(ex) { "Failed to render $child" } } } - }.launchIn(updateScope) + } } } is Composite -> compositeFactory(this, obj) From d916dde6a729f7ce33677f96b1db6addfa138ede Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 13 Aug 2021 22:14:43 +0300 Subject: [PATCH 09/15] Update to DataForge and plotly release versions --- build.gradle.kts | 2 +- visionforge-plotly/build.gradle.kts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 08ad66d8..c3f05f5f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,7 +2,7 @@ plugins { id("ru.mipt.npm.gradle.project") } -val dataforgeVersion by extra("0.5.0-dev-11") +val dataforgeVersion by extra("0.5.0") val fxVersion by extra("11") allprojects { diff --git a/visionforge-plotly/build.gradle.kts b/visionforge-plotly/build.gradle.kts index 41e541db..b0b5a9c2 100644 --- a/visionforge-plotly/build.gradle.kts +++ b/visionforge-plotly/build.gradle.kts @@ -2,7 +2,7 @@ plugins { id("ru.mipt.npm.gradle.mpp") } -val plotlyVersion = "0.5.0-dev-1" +val plotlyVersion = "0.5.0" kscience { useSerialization() From 60cec91ab8fab2b8ca0e7a00b92a775bb1ac50ba Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 14 Aug 2021 14:17:54 +0300 Subject: [PATCH 10/15] Update js examples --- build.gradle.kts | 1 + .../src/main/kotlin/JsPlaygroundApp.kt | 57 +++++++++++++++++-- .../src/main/kotlin/plotlyComponent.kt | 36 ++++++++++++ .../kscience/visionforge/plotly/plotlyJs.kt | 4 +- 4 files changed, 91 insertions(+), 7 deletions(-) create mode 100644 demo/js-playground/src/main/kotlin/plotlyComponent.kt diff --git a/build.gradle.kts b/build.gradle.kts index c3f05f5f..5c2557bd 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,6 @@ plugins { id("ru.mipt.npm.gradle.project") + kotlin("multiplatform") version "1.5.30-RC" apply false } val dataforgeVersion by extra("0.5.0") diff --git a/demo/js-playground/src/main/kotlin/JsPlaygroundApp.kt b/demo/js-playground/src/main/kotlin/JsPlaygroundApp.kt index ec28649c..509eb0ab 100644 --- a/demo/js-playground/src/main/kotlin/JsPlaygroundApp.kt +++ b/demo/js-playground/src/main/kotlin/JsPlaygroundApp.kt @@ -2,16 +2,22 @@ import kotlinx.browser.document import kotlinx.css.* import react.child import react.dom.render +import ringui.SmartTabs +import ringui.Tab import space.kscience.dataforge.context.Context import space.kscience.gdml.GdmlShowCase +import space.kscience.plotly.scatter import space.kscience.visionforge.Application import space.kscience.visionforge.VisionClient import space.kscience.visionforge.gdml.toVision +import space.kscience.visionforge.plotly.PlotlyPlugin import space.kscience.visionforge.ring.ThreeCanvasWithControls import space.kscience.visionforge.ring.ThreeWithControlsPlugin +import space.kscience.visionforge.solid.* import space.kscience.visionforge.startApplication import styled.css import styled.styledDiv +import kotlin.random.Random private class JsPlaygroundApp : Application { @@ -20,24 +26,65 @@ private class JsPlaygroundApp : Application { val playgroundContext = Context { plugin(ThreeWithControlsPlugin) plugin(VisionClient) + plugin(PlotlyPlugin) } val element = document.getElementById("playground") ?: error("Element with id 'playground' not found on page") val visionOfD0 = GdmlShowCase.babyIaxo().toVision() + val random = Random(112233) + val visionOfSpheres = SolidGroup { + repeat(100) { + sphere(5, name = "sphere[$it]") { + x = random.nextDouble(-300.0, 300.0) + y = random.nextDouble(-300.0, 300.0) + z = random.nextDouble(-300.0, 300.0) + material { + color(random.nextInt()) + } + detail = 16 + } + } + } + render(element) { styledDiv { - css{ + css { padding(0.pt) margin(0.pt) height = 100.vh width = 100.vw } - child(ThreeCanvasWithControls) { - attrs { - context = playgroundContext - solid = visionOfD0 + SmartTabs("D0") { + Tab("D0") { + child(ThreeCanvasWithControls) { + attrs { + context = playgroundContext + solid = visionOfD0 + } + } + } + Tab("spheres") { + child(ThreeCanvasWithControls) { + attrs { + context = playgroundContext + solid = visionOfSpheres + } + } + } + Tab("plotly"){ + Plotly{ + attrs { + context = playgroundContext + plot = space.kscience.plotly.Plotly.plot { + scatter { + x(1, 2, 3) + y(5, 8, 7) + } + } + } + } } } } diff --git a/demo/js-playground/src/main/kotlin/plotlyComponent.kt b/demo/js-playground/src/main/kotlin/plotlyComponent.kt new file mode 100644 index 00000000..7e456a69 --- /dev/null +++ b/demo/js-playground/src/main/kotlin/plotlyComponent.kt @@ -0,0 +1,36 @@ +import kotlinx.css.* +import org.w3c.dom.Element +import org.w3c.dom.HTMLElement +import react.RProps +import react.functionalComponent +import react.useEffect +import react.useRef +import space.kscience.dataforge.context.Context +import space.kscience.plotly.Plot +import space.kscience.plotly.plot +import styled.css +import styled.styledDiv + +external interface PlotlyProps: RProps{ + var context: Context + var plot: Plot? +} + + +val Plotly = functionalComponent("Plotly"){props -> + val elementRef = useRef(null) + + useEffect(props.plot, elementRef) { + val element = elementRef.current as? HTMLElement ?: error("Plotly element not found") + props.plot?.let { element.plot(it)} + } + + styledDiv { + css { + maxWidth = 100.vw + maxHeight = 100.vh + flex(1.0) + } + ref = elementRef + } +} \ No newline at end of file diff --git a/visionforge-plotly/src/jsMain/kotlin/space/kscience/visionforge/plotly/plotlyJs.kt b/visionforge-plotly/src/jsMain/kotlin/space/kscience/visionforge/plotly/plotlyJs.kt index 21b447ef..e0cea767 100644 --- a/visionforge-plotly/src/jsMain/kotlin/space/kscience/visionforge/plotly/plotlyJs.kt +++ b/visionforge-plotly/src/jsMain/kotlin/space/kscience/visionforge/plotly/plotlyJs.kt @@ -31,8 +31,8 @@ public actual class PlotlyPlugin : VisionPlugin(), ElementVisionRenderer { override fun render(element: Element, vision: Vision, meta: Meta) { val plot = (vision as? VisionOfPlotly)?.plot ?: error("VisionOfPlotly expected but ${vision::class} found") val config = PlotlyConfig.read(meta) - println(plot.meta) - println(plot.data[0].toMeta()) +// println(plot.meta) +// println(plot.data[0].toMeta()) element.plot(plot, config) } From a33d9d19464076e382210fa9e60a7e622d851894 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 14 Aug 2021 15:54:16 +0300 Subject: [PATCH 11/15] fix for Plotly vision serialization --- .../space/kscience/visionforge/html/VisionTagConsumer.kt | 2 ++ .../space/kscience/visionforge/markup/VisionOfMarkup.kt | 4 ++++ .../space/kscience/visionforge/plotly/VisionOfPlotly.kt | 9 +++++---- .../kotlin/space/kscience/visionforge/solid/Solids.kt | 2 +- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/VisionTagConsumer.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/VisionTagConsumer.kt index a0b64d8b..035b6a6e 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/VisionTagConsumer.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/html/VisionTagConsumer.kt @@ -11,6 +11,7 @@ import space.kscience.dataforge.names.NameToken import space.kscience.dataforge.names.asName import space.kscience.visionforge.Vision import space.kscience.visionforge.VisionManager +import space.kscience.visionforge.root import kotlin.collections.set @DslMarker @@ -84,6 +85,7 @@ public abstract class VisionTagConsumer( ): T { val output = VisionOutput(manager) val vision = output.visionProvider() + vision.root(manager) return vision(name, vision, output.meta) } diff --git a/visionforge-markdown/src/commonMain/kotlin/space/kscience/visionforge/markup/VisionOfMarkup.kt b/visionforge-markdown/src/commonMain/kotlin/space/kscience/visionforge/markup/VisionOfMarkup.kt index fd1dcd80..89a6836e 100644 --- a/visionforge-markdown/src/commonMain/kotlin/space/kscience/visionforge/markup/VisionOfMarkup.kt +++ b/visionforge-markdown/src/commonMain/kotlin/space/kscience/visionforge/markup/VisionOfMarkup.kt @@ -5,6 +5,7 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.modules.SerializersModule import kotlinx.serialization.modules.polymorphic import kotlinx.serialization.modules.subclass +import space.kscience.dataforge.meta.MutableMeta import space.kscience.dataforge.meta.string import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.asName @@ -18,6 +19,9 @@ public class VisionOfMarkup( public val format: String = COMMONMARK_FORMAT ) : VisionBase() { + //FIXME to be removed after https://github.com/Kotlin/kotlinx.serialization/issues/1602 fix + override var properties: MutableMeta? = null + //TODO add templates public var content: String? diff --git a/visionforge-plotly/src/commonMain/kotlin/space/kscience/visionforge/plotly/VisionOfPlotly.kt b/visionforge-plotly/src/commonMain/kotlin/space/kscience/visionforge/plotly/VisionOfPlotly.kt index 8874c694..99df2e84 100644 --- a/visionforge-plotly/src/commonMain/kotlin/space/kscience/visionforge/plotly/VisionOfPlotly.kt +++ b/visionforge-plotly/src/commonMain/kotlin/space/kscience/visionforge/plotly/VisionOfPlotly.kt @@ -2,16 +2,19 @@ package space.kscience.visionforge.plotly import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +import space.kscience.dataforge.meta.MutableMeta import space.kscience.dataforge.misc.DFExperimental import space.kscience.plotly.Plot import space.kscience.plotly.Plotly import space.kscience.visionforge.VisionBase import space.kscience.visionforge.html.VisionOutput -import space.kscience.visionforge.root @Serializable @SerialName("vision.plotly") public class VisionOfPlotly private constructor() : VisionBase() { + //FIXME to be removed after https://github.com/Kotlin/kotlinx.serialization/issues/1602 fix + override var properties: MutableMeta? = null + public constructor(plot: Plot) : this() { properties = plot.meta } @@ -23,6 +26,4 @@ public fun Plot.asVision(): VisionOfPlotly = VisionOfPlotly(this) @DFExperimental public inline fun VisionOutput.plotly( block: Plot.() -> Unit, -): VisionOfPlotly = VisionOfPlotly(Plotly.plot(block)).apply { - root(this@plotly.manager) -} \ No newline at end of file +): VisionOfPlotly = VisionOfPlotly(Plotly.plot(block)) \ No newline at end of file diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Solids.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Solids.kt index 67224699..ca83f00f 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Solids.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/Solids.kt @@ -69,4 +69,4 @@ public class Solids(meta: Meta) : VisionPlugin(meta) { @VisionBuilder @DFExperimental public inline fun VisionOutput.solid(block: SolidGroup.() -> Unit): SolidGroup = - SolidGroup().apply(block).apply { root(this@solid.manager) } + SolidGroup().apply(block) From f99a359e246197b17a3cded07e05b4b08502d4ab Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 14 Aug 2021 21:31:36 +0300 Subject: [PATCH 12/15] Fix color resolution --- build.gradle.kts | 4 +-- demo/playground/build.gradle.kts | 4 +-- .../src/jvmMain/kotlin/randomSpheres.kt | 2 +- .../visionforge/solid/ColorAccessor.kt | 13 ++++---- .../kscience/visionforge/solid/SolidBase.kt | 6 ---- .../visionforge/solid/three/ThreeMaterials.kt | 30 ++++++++++--------- 6 files changed, 27 insertions(+), 32 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 5c2557bd..2dc7926a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,9 +1,9 @@ plugins { id("ru.mipt.npm.gradle.project") - kotlin("multiplatform") version "1.5.30-RC" apply false +// kotlin("multiplatform") version "1.5.30-RC" apply false } -val dataforgeVersion by extra("0.5.0") +val dataforgeVersion by extra("0.5.1") val fxVersion by extra("11") allprojects { diff --git a/demo/playground/build.gradle.kts b/demo/playground/build.gradle.kts index 73fd2aa7..68ea5f9a 100644 --- a/demo/playground/build.gradle.kts +++ b/demo/playground/build.gradle.kts @@ -20,7 +20,7 @@ kotlin { this.outputFileName = "js/visionforge-playground.js" } commonWebpackConfig { - sourceMaps = false + sourceMaps = true cssSupport.enabled = false } } @@ -37,7 +37,7 @@ kotlin { } afterEvaluate { - val jsBrowserDistribution by tasks.getting + val jsBrowserDistribution = tasks.getByName("jsBrowserDevelopmentExecutableDistribution") tasks.getByName("jvmProcessResources") { dependsOn(jsBrowserDistribution) diff --git a/demo/playground/src/jvmMain/kotlin/randomSpheres.kt b/demo/playground/src/jvmMain/kotlin/randomSpheres.kt index 383f3d3a..0185bdc8 100644 --- a/demo/playground/src/jvmMain/kotlin/randomSpheres.kt +++ b/demo/playground/src/jvmMain/kotlin/randomSpheres.kt @@ -17,7 +17,7 @@ fun main() { context.makeVisionFile( Paths.get("randomSpheres.html"), - resourceLocation = ResourceLocation.EMBED + resourceLocation = ResourceLocation.SYSTEM ) { h1 { +"Happy new year!" } div { diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/ColorAccessor.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/ColorAccessor.kt index f06a7c6f..57f868f2 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/ColorAccessor.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/ColorAccessor.kt @@ -2,16 +2,15 @@ package space.kscience.visionforge.solid import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.plus -import space.kscience.dataforge.values.MutableValueProvider -import space.kscience.dataforge.values.Value -import space.kscience.dataforge.values.asValue -import space.kscience.dataforge.values.string +import space.kscience.dataforge.values.* import space.kscience.visionforge.Colors import space.kscience.visionforge.VisionBuilder @VisionBuilder -public class ColorAccessor(private val provider: MutableValueProvider, private val colorKey: Name) : - MutableValueProvider { +public class ColorAccessor( + private val provider: MutableValueProvider, + private val colorKey: Name +) : MutableValueProvider { public var value: Value? get() = provider.getValue(colorKey) set(value) { @@ -26,7 +25,7 @@ public class ColorAccessor(private val provider: MutableValueProvider, private v } public var ColorAccessor?.string: String? - get() = this?.value?.string + get() = this?.value?.let { if(it == Null) null else it.string } set(value) { this?.value = value?.asValue() } diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidBase.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidBase.kt index 7ca5c45d..021079d3 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidBase.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/SolidBase.kt @@ -5,7 +5,6 @@ import kotlinx.serialization.Serializable import space.kscience.dataforge.meta.MutableMeta import space.kscience.dataforge.meta.descriptors.MetaDescriptor import space.kscience.visionforge.VisionBase -import space.kscience.visionforge.VisionChange @Serializable @SerialName("solid") @@ -14,9 +13,4 @@ public open class SolidBase : VisionBase(), Solid { override var properties: MutableMeta? = null override val descriptor: MetaDescriptor get() = Solid.descriptor - - override fun update(change: VisionChange) { - updatePosition(change.properties) - super.update(change) - } } diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeMaterials.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeMaterials.kt index dc0ab967..522c0362 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeMaterials.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeMaterials.kt @@ -5,10 +5,7 @@ import info.laht.threekt.materials.Material import info.laht.threekt.materials.MeshBasicMaterial import info.laht.threekt.math.Color import info.laht.threekt.objects.Mesh -import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.meta.boolean -import space.kscience.dataforge.meta.double -import space.kscience.dataforge.meta.get +import space.kscience.dataforge.meta.* import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.asName import space.kscience.dataforge.values.* @@ -100,18 +97,23 @@ public object ThreeMaterials { /** * Compute color */ -public fun Meta.threeColor(): Color = getValue(Name.EMPTY)?.let { value -> - if (value.type == ValueType.NUMBER) { - val int = value.int - Color(int) +public fun Meta.threeColor(): Color? { + val value = getValue(Name.EMPTY) + return if (isLeaf) { + when { + value == null -> null + value === Null -> null + value.type == ValueType.NUMBER -> Color(value.int) + else -> Color(value.string) + } } else { - Color(value.string) + Color( + getValue(Colors.RED_KEY.asName())?.int ?: 0, + getValue(Colors.GREEN_KEY.asName())?.int ?: 0, + getValue(Colors.BLUE_KEY.asName())?.int ?: 0 + ) } -} ?: Color( - getValue(Colors.RED_KEY.asName())?.int ?: 0, - getValue(Colors.GREEN_KEY.asName())?.int ?: 0, - getValue(Colors.BLUE_KEY.asName())?.int ?: 0 -) +} private var Material.cached: Boolean get() = userData["cached"] == true From af327c17e3300fa4479a98f38db9f80bc247552e Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 15 Aug 2021 18:15:17 +0300 Subject: [PATCH 13/15] Fixes and bouncing ball demo. --- .../src/main/kotlin/JsPlaygroundApp.kt | 49 +++++- .../visionforge/bootstrap/outputConfig.kt | 2 +- .../bootstrap/visionPropertyEditor.kt | 6 +- .../visionforge/react/MultiSelectChooser.kt | 8 +- .../visionforge/react/PropertyEditor.kt | 151 ++++++------------ .../visionforge/react/RangeValueChooser.kt | 24 +-- .../visionforge/react/valueChooser.kt | 57 +++---- .../ThreeViewWithControls.kt | 2 - .../ringPropertyEditor.kt | 6 +- .../ringThreeControls.kt | 2 +- .../space/kscience/visionforge/Vision.kt | 16 ++ .../space/kscience/visionforge/VisionBase.kt | 2 +- .../visionforge/markup/VisionOfMarkup.kt | 9 +- .../visionforge/markup/MarkupPlugin.kt | 25 ++- .../solid/specifications/Canvas3DOptions.kt | 6 +- .../visionforge/solid/three/ThreeCanvas.kt | 64 ++++---- 16 files changed, 214 insertions(+), 215 deletions(-) diff --git a/demo/js-playground/src/main/kotlin/JsPlaygroundApp.kt b/demo/js-playground/src/main/kotlin/JsPlaygroundApp.kt index 509eb0ab..74ff9fd9 100644 --- a/demo/js-playground/src/main/kotlin/JsPlaygroundApp.kt +++ b/demo/js-playground/src/main/kotlin/JsPlaygroundApp.kt @@ -1,4 +1,8 @@ import kotlinx.browser.document +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.delay +import kotlinx.coroutines.isActive +import kotlinx.coroutines.launch import kotlinx.css.* import react.child import react.dom.render @@ -17,6 +21,7 @@ import space.kscience.visionforge.solid.* import space.kscience.visionforge.startApplication import styled.css import styled.styledDiv +import kotlin.math.sqrt import kotlin.random.Random private class JsPlaygroundApp : Application { @@ -31,9 +36,38 @@ private class JsPlaygroundApp : Application { val element = document.getElementById("playground") ?: error("Element with id 'playground' not found on page") + val bouncingSphere = SolidGroup { + sphere(5.0, "ball") { + detail = 16 + color("red") + val h = 100.0 + y = h + GlobalScope.launch { + val g = 10.0 + val dt = 0.1 + var time = 0.0 + var velocity = 0.0 + while (isActive) { + delay(20) + time += dt + velocity -= g * dt + y = y.toDouble() + velocity * dt + if (y.toDouble() <= 2.5){ + velocity = sqrt(2*g*h) + } + } + } + } + + box(200, 5, 200, name = "floor"){ + y = -2.5 + } + } + val visionOfD0 = GdmlShowCase.babyIaxo().toVision() val random = Random(112233) + val visionOfSpheres = SolidGroup { repeat(100) { sphere(5, name = "sphere[$it]") { @@ -56,7 +90,16 @@ private class JsPlaygroundApp : Application { height = 100.vh width = 100.vw } - SmartTabs("D0") { + SmartTabs("gravity") { + Tab("gravity") { + child(ThreeCanvasWithControls) { + attrs { + context = playgroundContext + solid = bouncingSphere + } + } + } + Tab("D0") { child(ThreeCanvasWithControls) { attrs { @@ -73,8 +116,8 @@ private class JsPlaygroundApp : Application { } } } - Tab("plotly"){ - Plotly{ + Tab("plotly") { + Plotly { attrs { context = playgroundContext plot = space.kscience.plotly.Plotly.plot { diff --git a/ui/bootstrap/src/main/kotlin/space/kscience/visionforge/bootstrap/outputConfig.kt b/ui/bootstrap/src/main/kotlin/space/kscience/visionforge/bootstrap/outputConfig.kt index 8088d2fd..7c7e7b37 100644 --- a/ui/bootstrap/src/main/kotlin/space/kscience/visionforge/bootstrap/outputConfig.kt +++ b/ui/bootstrap/src/main/kotlin/space/kscience/visionforge/bootstrap/outputConfig.kt @@ -66,7 +66,7 @@ public val CanvasControls: FunctionComponent = functionalCo } } propertyEditor( - ownProperties = props.canvasOptions, + ownProperties = props.canvasOptions.meta, allProperties = props.canvasOptions.meta.withDefault(Canvas3DOptions.descriptor.defaultNode), descriptor = Canvas3DOptions.descriptor, expanded = false diff --git a/ui/bootstrap/src/main/kotlin/space/kscience/visionforge/bootstrap/visionPropertyEditor.kt b/ui/bootstrap/src/main/kotlin/space/kscience/visionforge/bootstrap/visionPropertyEditor.kt index 65255af6..a65c1f42 100644 --- a/ui/bootstrap/src/main/kotlin/space/kscience/visionforge/bootstrap/visionPropertyEditor.kt +++ b/ui/bootstrap/src/main/kotlin/space/kscience/visionforge/bootstrap/visionPropertyEditor.kt @@ -4,10 +4,13 @@ import org.w3c.dom.Element import react.RBuilder import react.dom.render import space.kscience.dataforge.meta.descriptors.MetaDescriptor -import space.kscience.visionforge.* +import space.kscience.visionforge.Vision +import space.kscience.visionforge.computeProperties +import space.kscience.visionforge.getStyle import space.kscience.visionforge.react.metaViewer import space.kscience.visionforge.react.propertyEditor import space.kscience.visionforge.solid.SolidReference +import space.kscience.visionforge.styles public fun RBuilder.visionPropertyEditor( vision: Vision, @@ -19,7 +22,6 @@ public fun RBuilder.visionPropertyEditor( propertyEditor( ownProperties = vision.meta, allProperties = vision.computeProperties(), - updateFlow = vision.propertyChanges, descriptor = descriptor, key = key ) diff --git a/ui/react/src/main/kotlin/space/kscience/visionforge/react/MultiSelectChooser.kt b/ui/react/src/main/kotlin/space/kscience/visionforge/react/MultiSelectChooser.kt index 9f698df7..84181340 100644 --- a/ui/react/src/main/kotlin/space/kscience/visionforge/react/MultiSelectChooser.kt +++ b/ui/react/src/main/kotlin/space/kscience/visionforge/react/MultiSelectChooser.kt @@ -10,7 +10,6 @@ import react.dom.attrs import react.dom.option import react.dom.select import react.functionalComponent -import react.useState import space.kscience.dataforge.meta.descriptors.allowedValues import space.kscience.dataforge.values.asValue import space.kscience.dataforge.values.string @@ -18,19 +17,16 @@ import space.kscience.dataforge.values.string @JsExport public val MultiSelectChooser: FunctionComponent = functionalComponent("MultiSelectChooser") { props -> - var selectedItems by useState { props.item?.value?.list ?: emptyList() } - val onChange: (Event) -> Unit = { event: Event -> val newSelected = (event.target as HTMLSelectElement).selectedOptions.asList() .map { (it as HTMLOptionElement).value.asValue() } - props.valueChanged?.invoke(newSelected.asValue()) - selectedItems = newSelected + props.meta.value = newSelected.asValue() } select { attrs { multiple = true - values = selectedItems.mapTo(HashSet()) { it.string } + values = (props.actual.value?.list ?: emptyList()).mapTo(HashSet()) { it.string } onChangeFunction = onChange } props.descriptor?.allowedValues?.forEach { optionValue -> diff --git a/ui/react/src/main/kotlin/space/kscience/visionforge/react/PropertyEditor.kt b/ui/react/src/main/kotlin/space/kscience/visionforge/react/PropertyEditor.kt index 47b0d818..9ff77f2c 100644 --- a/ui/react/src/main/kotlin/space/kscience/visionforge/react/PropertyEditor.kt +++ b/ui/react/src/main/kotlin/space/kscience/visionforge/react/PropertyEditor.kt @@ -1,14 +1,5 @@ package space.kscience.visionforge.react -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.channels.awaitClose -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.properties.TextDecoration import kotlinx.html.js.onClickFunction @@ -22,7 +13,6 @@ import space.kscience.dataforge.meta.descriptors.MetaDescriptor import space.kscience.dataforge.meta.descriptors.ValueRequirement import space.kscience.dataforge.meta.descriptors.get import space.kscience.dataforge.names.* -import space.kscience.dataforge.values.Value import space.kscience.visionforge.hidden import styled.css import styled.styledButton @@ -32,17 +22,17 @@ import styled.styledSpan public external interface PropertyEditorProps : RProps { /** - * Root config object - always non null + * Root config object - always non-null */ - public var ownProperties: MutableMetaProvider + public var meta: ObservableMutableMeta /** * Provide default item (greyed out if used) */ - public var allProperties: MetaProvider? + public var withDefault: MetaProvider /** - * Full path to the displayed node in [ownProperties]. Could be empty + * Full path to the displayed node in [meta]. Could be empty */ public var name: Name @@ -51,16 +41,6 @@ public external interface PropertyEditorProps : RProps { */ public var descriptor: MetaDescriptor? - /** - * A coroutine scope for updates - */ - public var scope: CoroutineScope? - - /** - * Flow names of updated properties - */ - public var updateFlow: Flow? - /** * Initial expanded state */ @@ -68,67 +48,59 @@ public external interface PropertyEditorProps : RProps { } private val PropertyEditorItem: FunctionComponent = - functionalComponent("ConfigEditorItem") { props -> + functionalComponent("PropertyEditorItem") { props -> propertyEditorItem(props) } private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) { var expanded: Boolean by useState { props.expanded ?: true } - val descriptor: MetaDescriptor? = props.descriptor?.get(props.name) - var ownProperty: Meta? by useState { props.ownProperties.getMeta(props.name) } - val actualMeta = props.allProperties?.getMeta(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) } + + val keys = useMemo(descriptor) { + buildSet { + descriptor?.children?.filterNot { + it.key.startsWith("@") || it.value.hidden + }?.forEach { + add(NameToken(it.key)) + } + //ownProperty?.items?.keys?.filterNot { it.body.startsWith("@") }?.let { addAll(it) } + } + } val token = props.name.lastOrNull()?.toString() ?: "Properties" fun update() { - ownProperty = props.ownProperties.getMeta(props.name) + ownProperty = props.meta.getOrCreate(props.name) } - if (props.updateFlow != null) { - useEffect(props.ownProperties, props.updateFlow) { - val updateJob = props.updateFlow!!.onEach { updatedName -> - if (updatedName == props.name) { - update() - } - }.launchIn(props.scope ?: GlobalScope) - cleanup { - updateJob.cancel() + useEffect(props.meta) { + props.meta.onChange(props) { updatedName -> + if (updatedName == props.name) { + update() } } + cleanup { + props.meta.removeListener(props) + } } val expanderClick: (Event) -> Unit = { expanded = !expanded } - val valueChanged: (Value?) -> Unit = { - if (it == null) { - props.ownProperties.remove(props.name) - } else { - props.ownProperties.setValue(props.name, it) - } - update() - } - val removeClick: (Event) -> Unit = { - props.ownProperties.remove(props.name) + props.meta.remove(props.name) update() } - val keys = buildSet { - descriptor?.children?.filterNot { - it.key.startsWith("@") || it.value.hidden - }?.forEach { - add(NameToken(it.key)) - } - //ownProperty?.items?.keys?.filterNot { it.body.startsWith("@") }?.let { addAll(it) } - } + flexRow { css { alignItems = Align.center } - if(keys.isNotEmpty()) { + if (keys.isNotEmpty()) { styledSpan { css { +TreeStyles.treeCaret @@ -144,25 +116,26 @@ private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) { styledSpan { css { +TreeStyles.treeLabel - if (ownProperty == null) { + if (ownProperty.isEmpty()) { +TreeStyles.treeLabelInactive } } +token } - if(!props.name.isEmpty() && descriptor?.valueRequirement != ValueRequirement.ABSENT) { + if (!props.name.isEmpty() && descriptor?.valueRequirement != ValueRequirement.ABSENT) { styledDiv { css { //+TreeStyles.resizeableInput width = 160.px margin(1.px, 5.px) } - valueChooser( - props.name, - actualMeta, - descriptor, - valueChanged - ) + ValueChooser{ + attrs { + this.descriptor = descriptor + this.meta = ownProperty + this.actual = props.withDefault.getMeta(props.name) ?: ownProperty + } + } } styledButton { @@ -184,7 +157,7 @@ private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) { } +"\u00D7" attrs { - if (ownProperty == null) { + if (ownProperty.isEmpty()) { disabled = true } else { onClickFunction = removeClick @@ -206,8 +179,8 @@ private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) { child(PropertyEditorItem) { attrs { this.key = props.name.toString() - this.ownProperties = props.ownProperties - this.allProperties = props.allProperties + this.meta = props.meta + this.withDefault = props.withDefault this.name = props.name + token this.descriptor = props.descriptor } @@ -217,74 +190,52 @@ private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) { } } } - } - @JsExport public val PropertyEditor: FunctionComponent = functionalComponent("PropertyEditor") { props -> child(PropertyEditorItem) { attrs { this.key = "" - this.ownProperties = props.ownProperties - this.allProperties = props.allProperties + this.meta = props.meta + this.withDefault = props.withDefault this.name = Name.EMPTY this.descriptor = props.descriptor - this.scope = props.scope this.expanded = props.expanded } } } public fun RBuilder.propertyEditor( - ownProperties: MutableMetaProvider, - allProperties: MetaProvider? = ownProperties, - updateFlow: Flow? = null, + ownProperties: ObservableMutableMeta, + allProperties: MetaProvider = ownProperties, descriptor: MetaDescriptor? = null, - scope: CoroutineScope? = null, key: Any? = null, expanded: Boolean? = null ) { child(PropertyEditor) { attrs { - this.ownProperties = ownProperties - this.allProperties = allProperties - this.updateFlow = updateFlow + this.meta = ownProperties + this.withDefault = allProperties this.descriptor = descriptor this.key = key?.toString() ?: "" - this.scope = scope this.expanded = expanded } } } -@OptIn(ExperimentalCoroutinesApi::class) -private fun ObservableMutableMeta.flowUpdates(): Flow = callbackFlow { - onChange(this) { name -> - launch { - send(name) - } - } - awaitClose { - removeListener(this) - } -} - - public fun RBuilder.configEditor( config: ObservableMutableMeta, - default: MetaProvider? = null, + default: MetaProvider = config, descriptor: MetaDescriptor? = null, key: Any? = null, - scope: CoroutineScope? = null, -): Unit = propertyEditor(config, default, config.flowUpdates(), descriptor, scope, key = key) +): Unit = propertyEditor(config, default, descriptor, key = key) public fun Element.configEditor( config: ObservableMutableMeta, + default: Meta = config, descriptor: MetaDescriptor? = null, - default: Meta? = null, key: Any? = null, - scope: CoroutineScope? = null, ): Unit = render(this) { - configEditor(config, default, descriptor, key, scope) + configEditor(config, default, descriptor, key = key) } \ No newline at end of file diff --git a/ui/react/src/main/kotlin/space/kscience/visionforge/react/RangeValueChooser.kt b/ui/react/src/main/kotlin/space/kscience/visionforge/react/RangeValueChooser.kt index 28513416..c753271f 100644 --- a/ui/react/src/main/kotlin/space/kscience/visionforge/react/RangeValueChooser.kt +++ b/ui/react/src/main/kotlin/space/kscience/visionforge/react/RangeValueChooser.kt @@ -10,6 +10,7 @@ import react.FunctionComponent import react.dom.attrs import react.functionalComponent import react.useState +import space.kscience.dataforge.meta.descriptors.ValueRequirement import space.kscience.dataforge.meta.double import space.kscience.dataforge.meta.get import space.kscience.dataforge.meta.string @@ -20,30 +21,32 @@ import styled.styledInput @JsExport public val RangeValueChooser: FunctionComponent = functionalComponent("RangeValueChooser") { props -> - var innerValue by useState(props.item.double) - var rangeDisabled: Boolean by useState(props.item == null) + var innerValue by useState(props.actual.double) + var rangeDisabled: Boolean by useState(props.meta.value == null) val handleDisable: (Event) -> Unit = { val checkBoxValue = (it.target as HTMLInputElement).checked rangeDisabled = !checkBoxValue - if(!checkBoxValue) { - props.valueChanged?.invoke(null) + props.meta.value = if(!checkBoxValue) { + null } else { - props.valueChanged?.invoke(innerValue?.asValue()) + innerValue?.asValue() } } val handleChange: (Event) -> Unit = { val newValue = (it.target as HTMLInputElement).value - props.valueChanged?.invoke(newValue.toDoubleOrNull()?.asValue()) + props.meta.value = newValue.toDoubleOrNull()?.asValue() innerValue = newValue.toDoubleOrNull() } flexRow { - styledInput(type = InputType.checkBox) { - attrs { - defaultChecked = rangeDisabled.not() - onChangeFunction = handleDisable + if(props.descriptor?.valueRequirement != ValueRequirement.REQUIRED) { + styledInput(type = InputType.checkBox) { + attrs { + defaultChecked = rangeDisabled.not() + onChangeFunction = handleDisable + } } } @@ -55,6 +58,7 @@ public val RangeValueChooser: FunctionComponent = disabled = rangeDisabled value = innerValue?.toString() ?: "" onChangeFunction = handleChange + consumer.onTagEvent(this, "input", handleChange) val minValue = props.descriptor?.attributes?.get("min").string minValue?.let { min = it diff --git a/ui/react/src/main/kotlin/space/kscience/visionforge/react/valueChooser.kt b/ui/react/src/main/kotlin/space/kscience/visionforge/react/valueChooser.kt index 2cfcc976..e29f0f8b 100644 --- a/ui/react/src/main/kotlin/space/kscience/visionforge/react/valueChooser.kt +++ b/ui/react/src/main/kotlin/space/kscience/visionforge/react/valueChooser.kt @@ -13,14 +13,13 @@ import org.w3c.dom.events.Event import react.* import react.dom.attrs import react.dom.option -import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.meta.boolean +import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.descriptors.MetaDescriptor import space.kscience.dataforge.meta.descriptors.allowedValues -import space.kscience.dataforge.meta.get -import space.kscience.dataforge.meta.string -import space.kscience.dataforge.names.Name -import space.kscience.dataforge.values.* +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.widgetType import styled.css @@ -28,23 +27,19 @@ import styled.styledInput import styled.styledSelect public external interface ValueChooserProps : RProps { - public var item: Meta? public var descriptor: MetaDescriptor? - - //public var nullable: Boolean? - public var valueChanged: ((Value?) -> Unit)? + public var meta: ObservableMutableMeta + public var actual: Meta } @JsExport public val StringValueChooser: FunctionComponent = functionalComponent("StringValueChooser") { props -> - var value by useState(props.item.string ?: "") + var value by useState(props.actual.string ?: "") val keyDown: (Event) -> Unit = { event -> if (event.type == "keydown" && event.asDynamic().key == "Enter") { value = (event.target as HTMLInputElement).value - if (value != props.item.string) { - props.valueChanged?.invoke(value.asValue()) - } + props.meta.value = value.asValue() } } val handleChange: (Event) -> Unit = { @@ -67,7 +62,7 @@ public val BooleanValueChooser: FunctionComponent = functionalComponent("BooleanValueChooser") { props -> val handleChange: (Event) -> Unit = { val newValue = (it.target as HTMLInputElement).checked - props.valueChanged?.invoke(newValue.asValue()) + props.meta.value = newValue.asValue() } styledInput(type = InputType.checkBox) { css { @@ -75,7 +70,7 @@ public val BooleanValueChooser: FunctionComponent = } attrs { //this.attributes["indeterminate"] = (props.item == null).toString() - defaultChecked = props.item.boolean ?: false + defaultChecked = props.actual.boolean ?: false onChangeFunction = handleChange } } @@ -84,7 +79,7 @@ public val BooleanValueChooser: FunctionComponent = @JsExport public val NumberValueChooser: FunctionComponent = functionalComponent("NumberValueChooser") { props -> - var innerValue by useState(props.item.string ?: "") + var innerValue by useState(props.actual.string ?: "") val keyDown: (Event) -> Unit = { event -> if (event.type == "keydown" && event.asDynamic().key == "Enter") { innerValue = (event.target as HTMLInputElement).value @@ -92,7 +87,7 @@ public val NumberValueChooser: FunctionComponent = if (number == null) { console.error("The input value $innerValue is not a number") } else { - props.valueChanged?.invoke(number.asValue()) + props.meta.value = number.asValue() } } } @@ -123,10 +118,10 @@ public val NumberValueChooser: FunctionComponent = @JsExport public val ComboValueChooser: FunctionComponent = functionalComponent("ComboValueChooser") { props -> - var selected by useState(props.item.string ?: "") + var selected by useState(props.actual.string ?: "") val handleChange: (Event) -> Unit = { selected = (it.target as HTMLSelectElement).value - props.valueChanged?.invoke(selected.asValue()) + props.meta.value = selected.asValue() } styledSelect { css { @@ -138,7 +133,7 @@ public val ComboValueChooser: FunctionComponent = } } attrs { - this.value = props.item?.string ?: "" + this.value = props.actual.string ?: "" multiple = false onChangeFunction = handleChange } @@ -149,14 +144,14 @@ public val ComboValueChooser: FunctionComponent = public val ColorValueChooser: FunctionComponent = functionalComponent("ColorValueChooser") { props -> var value by useState( - props.item?.value?.let { value -> + props.actual.value?.let { value -> if (value.type == ValueType.NUMBER) Colors.rgbToString(value.int) else value.string } ?: "#000000" ) val handleChange: (Event) -> Unit = { value = (it.target as HTMLInputElement).value - props.valueChanged?.invoke(value.asValue()) + props.meta.value = value.asValue() } styledInput(type = InputType.color) { css { @@ -189,19 +184,3 @@ public val ValueChooser: FunctionComponent = functionalCompon else -> child(StringValueChooser, props) } } - -internal fun RBuilder.valueChooser( - name: Name, - item: Meta?, - descriptor: MetaDescriptor? = null, - callback: (Value?) -> Unit, -) { - child(ValueChooser) { - attrs { - key = name.toString() - this.item = item - this.descriptor = descriptor - this.valueChanged = callback - } - } -} diff --git a/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ThreeViewWithControls.kt b/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ThreeViewWithControls.kt index f8439a07..a14d23ca 100644 --- a/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ThreeViewWithControls.kt +++ b/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ThreeViewWithControls.kt @@ -15,7 +15,6 @@ import space.kscience.dataforge.names.isEmpty import space.kscience.dataforge.names.length import space.kscience.visionforge.VisionGroup import space.kscience.visionforge.computeProperties -import space.kscience.visionforge.propertyChanges import space.kscience.visionforge.react.ThreeCanvasComponent import space.kscience.visionforge.react.flexColumn import space.kscience.visionforge.react.flexRow @@ -137,7 +136,6 @@ public val ThreeCanvasWithControls: FunctionComponent = functional } } propertyEditor( - ownProperties = props.options, + ownProperties = props.options.meta, allProperties = props.options.meta.withDefault(Canvas3DOptions.descriptor.defaultNode), descriptor = Canvas3DOptions.descriptor, expanded = false diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/Vision.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/Vision.kt index e7bdd301..924dfdd1 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/Vision.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/Vision.kt @@ -12,10 +12,12 @@ import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.misc.Type import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.asName +import space.kscience.dataforge.names.startsWith import space.kscience.dataforge.values.Value import space.kscience.dataforge.values.asValue import space.kscience.dataforge.values.boolean import space.kscience.visionforge.Vision.Companion.TYPE +import kotlin.reflect.KProperty1 /** * A root type for display hierarchy @@ -129,3 +131,17 @@ public var Vision.visible: Boolean? get() = getPropertyValue(Vision.VISIBLE_KEY)?.boolean set(value) = meta.setValue(Vision.VISIBLE_KEY, value?.asValue()) + +public fun V.useProperty( + property: KProperty1, + 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)) + } + } +} \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionBase.kt b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionBase.kt index c7d9413c..74f173ae 100644 --- a/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionBase.kt +++ b/visionforge-core/src/commonMain/kotlin/space/kscience/visionforge/VisionBase.kt @@ -44,7 +44,7 @@ public open class VisionBase( } @Transient - private val listeners = HashSet() + private val listeners: MutableList = mutableListOf() private inner class VisionProperties(val pathName: Name) : ObservableMutableMeta { diff --git a/visionforge-markdown/src/commonMain/kotlin/space/kscience/visionforge/markup/VisionOfMarkup.kt b/visionforge-markdown/src/commonMain/kotlin/space/kscience/visionforge/markup/VisionOfMarkup.kt index 89a6836e..58708ab1 100644 --- a/visionforge-markdown/src/commonMain/kotlin/space/kscience/visionforge/markup/VisionOfMarkup.kt +++ b/visionforge-markdown/src/commonMain/kotlin/space/kscience/visionforge/markup/VisionOfMarkup.kt @@ -11,7 +11,6 @@ import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.asName import space.kscience.visionforge.Vision import space.kscience.visionforge.VisionBase -import space.kscience.visionforge.setProperty @Serializable @SerialName("vision.markup") @@ -20,15 +19,11 @@ public class VisionOfMarkup( ) : VisionBase() { //FIXME to be removed after https://github.com/Kotlin/kotlinx.serialization/issues/1602 fix - override var properties: MutableMeta? = null + protected override var properties: MutableMeta? = null //TODO add templates - public var content: String? - get() = meta.getMeta(CONTENT_PROPERTY_KEY).string - set(value) { - setProperty(CONTENT_PROPERTY_KEY, value) - } + public var content: String? by meta.string(CONTENT_PROPERTY_KEY) public companion object { public val CONTENT_PROPERTY_KEY: Name = "content".asName() diff --git a/visionforge-markdown/src/jsMain/kotlin/space/kscience/visionforge/markup/MarkupPlugin.kt b/visionforge-markdown/src/jsMain/kotlin/space/kscience/visionforge/markup/MarkupPlugin.kt index 30b8b848..d26488c5 100644 --- a/visionforge-markdown/src/jsMain/kotlin/space/kscience/visionforge/markup/MarkupPlugin.kt +++ b/visionforge-markdown/src/jsMain/kotlin/space/kscience/visionforge/markup/MarkupPlugin.kt @@ -1,16 +1,19 @@ package space.kscience.visionforge.markup import kotlinx.browser.document +import kotlinx.dom.clear +import kotlinx.html.dom.append import kotlinx.serialization.modules.SerializersModule +import org.intellij.markdown.flavours.commonmark.CommonMarkFlavourDescriptor +import org.intellij.markdown.flavours.gfm.GFMFlavourDescriptor import org.w3c.dom.Element import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.PluginFactory import space.kscience.dataforge.context.PluginTag import space.kscience.dataforge.meta.Meta -import space.kscience.visionforge.ElementVisionRenderer -import space.kscience.visionforge.Vision -import space.kscience.visionforge.VisionClient -import space.kscience.visionforge.VisionPlugin +import space.kscience.visionforge.* +import space.kscience.visionforge.markup.VisionOfMarkup.Companion.COMMONMARK_FORMAT +import space.kscience.visionforge.markup.VisionOfMarkup.Companion.GFM_FORMAT import kotlin.reflect.KClass public class MarkupPlugin : VisionPlugin(), ElementVisionRenderer { @@ -26,8 +29,20 @@ public class MarkupPlugin : VisionPlugin(), ElementVisionRenderer { override fun render(element: Element, vision: Vision, meta: Meta) { require(vision is VisionOfMarkup) { "The vision is not a markup vision" } val div = document.createElement("div") + val flavour = when (vision.format) { + COMMONMARK_FORMAT -> CommonMarkFlavourDescriptor() + GFM_FORMAT -> GFMFlavourDescriptor() + //TODO add new formats via plugins + else-> error("Format ${vision.format} not recognized") + } + vision.useProperty(VisionOfMarkup::content) { + div.clear() + div.append { + markdown(flavour) { vision.content ?: "" } + + } + } element.append(div) - TODO() } public companion object : PluginFactory { diff --git a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Canvas3DOptions.kt b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Canvas3DOptions.kt index 01f6e74e..4a819ff7 100644 --- a/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Canvas3DOptions.kt +++ b/visionforge-solid/src/commonMain/kotlin/space/kscience/visionforge/solid/specifications/Canvas3DOptions.kt @@ -5,7 +5,6 @@ import space.kscience.dataforge.meta.descriptors.MetaDescriptor import space.kscience.dataforge.meta.descriptors.scheme import space.kscience.dataforge.meta.descriptors.value import space.kscience.dataforge.names.Name -import space.kscience.dataforge.values.ValueType import space.kscience.visionforge.hide import space.kscience.visionforge.widgetType @@ -21,20 +20,21 @@ public class Clipping : Scheme() { attributes["min"] = 0.0 attributes["max"] = 1.0 attributes["step"] = 0.01 + default(1.0) } value(Clipping::y) { widgetType = "range" attributes["min"] = 0.0 attributes["max"] = 1.0 attributes["step"] = 0.01 - + default(1.0) } value(Clipping::z) { widgetType = "range" attributes["min"] = 0.0 attributes["max"] = 1.0 attributes["step"] = 0.01 - + default(1.0) } } } diff --git a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCanvas.kt b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCanvas.kt index 2cfcadf5..0d223987 100644 --- a/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCanvas.kt +++ b/visionforge-threejs/src/main/kotlin/space/kscience/visionforge/solid/three/ThreeCanvas.kt @@ -168,42 +168,40 @@ public class ThreeCanvas( } //Clipping planes - options.meta.onChange(this@ThreeCanvas) { name-> - if (name.startsWith(Canvas3DOptions::clipping.name.asName())) { - val clipping = options.clipping - if (!clipping.meta.isEmpty()) { - renderer.localClippingEnabled = true - boundingBox?.let { boundingBox -> - val xClippingPlane = clipping.x?.let { - val absoluteValue = boundingBox.min.x + (boundingBox.max.x - boundingBox.min.x) * it - Plane(Vector3(-1.0, 0.0, 0.0), absoluteValue) - - } - val yClippingPlane = clipping.y?.let { - val absoluteValue = boundingBox.min.y + (boundingBox.max.y - boundingBox.min.y) * it - Plane(Vector3(0.0, -1.0, 0.0), absoluteValue) - } - - val zClippingPlane = clipping.z?.let { - val absoluteValue = boundingBox.min.z + (boundingBox.max.z - boundingBox.min.z) * it - Plane(Vector3(0.0, 0.0, -1.0), absoluteValue) - } - renderer.clippingPlanes = - listOfNotNull(xClippingPlane, yClippingPlane, zClippingPlane).toTypedArray() + options.useProperty(Canvas3DOptions::clipping){clipping -> + if (!clipping.meta.isEmpty()) { + renderer.localClippingEnabled = true + boundingBox?.let { boundingBox -> + val xClippingPlane = clipping.x?.let { + val absoluteValue = boundingBox.min.x + (boundingBox.max.x - boundingBox.min.x) * it + Plane(Vector3(-1.0, 0.0, 0.0), absoluteValue) + } + val yClippingPlane = clipping.y?.let { + val absoluteValue = boundingBox.min.y + (boundingBox.max.y - boundingBox.min.y) * it + Plane(Vector3(0.0, -1.0, 0.0), absoluteValue) } - } else { - renderer.localClippingEnabled = false - } - } else if (name.startsWith(Canvas3DOptions::size.name.asName())) { - canvas.style.apply { - minWidth = "${options.size.minWith.toInt()}px" - maxWidth = "${options.size.maxWith.toInt()}px" - minHeight = "${options.size.minHeight.toInt()}px" - maxHeight = "${options.size.maxHeight.toInt()}px" - } - } + val zClippingPlane = clipping.z?.let { + val absoluteValue = boundingBox.min.z + (boundingBox.max.z - boundingBox.min.z) * it + Plane(Vector3(0.0, 0.0, -1.0), absoluteValue) + } + renderer.clippingPlanes = + listOfNotNull(xClippingPlane, yClippingPlane, zClippingPlane).toTypedArray() + } + } else { + renderer.localClippingEnabled = false + } } + + options.useProperty(Canvas3DOptions::size){ + canvas.style.apply { + minWidth = "${options.size.minWith.toInt()}px" + maxWidth = "${options.size.maxWith.toInt()}px" + minHeight = "${options.size.minHeight.toInt()}px" + maxHeight = "${options.size.maxHeight.toInt()}px" + } + } + } /** From 95459422d6b7c18e1901872ea232272a10e00182 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 15 Aug 2021 18:45:36 +0300 Subject: [PATCH 14/15] Fixes and bouncing ball demo. --- .../src/main/kotlin/JsPlaygroundApp.kt | 60 ++++++++++++------- .../src/main/kotlin/plotlyComponent.kt | 4 +- 2 files changed, 41 insertions(+), 23 deletions(-) diff --git a/demo/js-playground/src/main/kotlin/JsPlaygroundApp.kt b/demo/js-playground/src/main/kotlin/JsPlaygroundApp.kt index 74ff9fd9..a54e48c9 100644 --- a/demo/js-playground/src/main/kotlin/JsPlaygroundApp.kt +++ b/demo/js-playground/src/main/kotlin/JsPlaygroundApp.kt @@ -9,11 +9,11 @@ import react.dom.render import ringui.SmartTabs import ringui.Tab import space.kscience.dataforge.context.Context -import space.kscience.gdml.GdmlShowCase +import space.kscience.plotly.models.Trace +import space.kscience.plotly.models.appendXY import space.kscience.plotly.scatter import space.kscience.visionforge.Application import space.kscience.visionforge.VisionClient -import space.kscience.visionforge.gdml.toVision import space.kscience.visionforge.plotly.PlotlyPlugin import space.kscience.visionforge.ring.ThreeCanvasWithControls import space.kscience.visionforge.ring.ThreeWithControlsPlugin @@ -36,6 +36,8 @@ private class JsPlaygroundApp : Application { val element = document.getElementById("playground") ?: error("Element with id 'playground' not found on page") + val bouncingSphereTrace = Trace() + val bouncingSphere = SolidGroup { sphere(5.0, "ball") { detail = 16 @@ -52,20 +54,19 @@ private class JsPlaygroundApp : Application { time += dt velocity -= g * dt y = y.toDouble() + velocity * dt - if (y.toDouble() <= 2.5){ - velocity = sqrt(2*g*h) + bouncingSphereTrace.appendXY(time, y) + if (y.toDouble() <= 2.5) { + //conservation of energy + velocity = sqrt(2 * g * h) } } } } - box(200, 5, 200, name = "floor"){ + box(200, 5, 200, name = "floor") { y = -2.5 } } - - val visionOfD0 = GdmlShowCase.babyIaxo().toVision() - val random = Random(112233) val visionOfSpheres = SolidGroup { @@ -92,22 +93,41 @@ private class JsPlaygroundApp : Application { } SmartTabs("gravity") { Tab("gravity") { - child(ThreeCanvasWithControls) { - attrs { - context = playgroundContext - solid = bouncingSphere + styledDiv { + css{ + height = 50.vh + } + child(ThreeCanvasWithControls) { + attrs { + context = playgroundContext + solid = bouncingSphere + } + } + } + styledDiv { + css{ + height = 40.vh + } + + Plotly { + attrs { + context = playgroundContext + plot = space.kscience.plotly.Plotly.plot { + traces(bouncingSphereTrace) + } + } } } } - Tab("D0") { - child(ThreeCanvasWithControls) { - attrs { - context = playgroundContext - solid = visionOfD0 - } - } - } +// Tab("D0") { +// child(ThreeCanvasWithControls) { +// attrs { +// context = playgroundContext +// solid = GdmlShowCase.babyIaxo().toVision() +// } +// } +// } Tab("spheres") { child(ThreeCanvasWithControls) { attrs { diff --git a/demo/js-playground/src/main/kotlin/plotlyComponent.kt b/demo/js-playground/src/main/kotlin/plotlyComponent.kt index 7e456a69..9480c68b 100644 --- a/demo/js-playground/src/main/kotlin/plotlyComponent.kt +++ b/demo/js-playground/src/main/kotlin/plotlyComponent.kt @@ -1,4 +1,4 @@ -import kotlinx.css.* +import kotlinx.css.flex import org.w3c.dom.Element import org.w3c.dom.HTMLElement import react.RProps @@ -27,8 +27,6 @@ val Plotly = functionalComponent("Plotly"){props -> styledDiv { css { - maxWidth = 100.vw - maxHeight = 100.vh flex(1.0) } ref = elementRef From 8a8bac48db286049661e1ddc49c5d99841ae3285 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 15 Aug 2021 20:04:21 +0300 Subject: [PATCH 15/15] Update examples --- .../visionforge/gdml/demo/GDMLAppComponent.kt | 54 +++++---- .../src/main/kotlin/JsPlaygroundApp.kt | 107 +++++++++--------- ui/ring/src/main/kotlin/ringui/Loader.kt | 19 ++++ .../src/main/kotlin/ringui/LoaderScreen.kt | 16 +++ .../ThreeViewWithControls.kt | 55 ++++++--- .../ThreeWithControlsPlugin.kt | 3 +- .../space/kscience/visionforge/Application.kt | 8 +- 7 files changed, 169 insertions(+), 93 deletions(-) create mode 100644 ui/ring/src/main/kotlin/ringui/Loader.kt create mode 100644 ui/ring/src/main/kotlin/ringui/LoaderScreen.kt diff --git a/demo/gdml/src/jsMain/kotlin/space/kscience/visionforge/gdml/demo/GDMLAppComponent.kt b/demo/gdml/src/jsMain/kotlin/space/kscience/visionforge/gdml/demo/GDMLAppComponent.kt index 4f6560b6..0c52257d 100644 --- a/demo/gdml/src/jsMain/kotlin/space/kscience/visionforge/gdml/demo/GDMLAppComponent.kt +++ b/demo/gdml/src/jsMain/kotlin/space/kscience/visionforge/gdml/demo/GDMLAppComponent.kt @@ -1,6 +1,9 @@ package space.kscience.visionforge.gdml.demo import kotlinx.browser.window +import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.Deferred +import org.w3c.files.File import org.w3c.files.FileReader import org.w3c.files.get import react.* @@ -27,32 +30,43 @@ external interface GDMLAppProps : RProps { @JsExport val GDMLApp = functionalComponent("GDMLApp") { props -> val visionManager = useMemo(props.context) { props.context.fetch(Solids).visionManager } - var vision: Solid? by useState { props.vision?.apply { root(visionManager) } } + var deferredVision: Deferred by useState { + CompletableDeferred(props.vision) + } - fun loadData(name: String, data: String) { - val parsedVision = when { - name.endsWith(".gdml") || name.endsWith(".xml") -> { - val gdml = Gdml.decodeFromString(data) - gdml.toVision().apply { - root(visionManager) - console.info("Marking layers for file $name") - markLayers() + fun readFileAsync(file: File): Deferred { + val deferred = CompletableDeferred() + FileReader().apply { + onload = { + val data = result as String + val name = file.name + val parsedVision = when { + name.endsWith(".gdml") || name.endsWith(".xml") -> { + val gdml = Gdml.decodeFromString(data) + gdml.toVision().apply { + root(visionManager) + console.info("Marking layers for file $name") + markLayers() + } + } + name.endsWith(".json") -> visionManager.decodeFromString(data) + else -> { + window.alert("File extension is not recognized: $name") + error("File extension is not recognized: $name") + } } + deferred.complete(parsedVision as? Solid ?: error("Parsed vision is not a solid")) } - name.endsWith(".json") -> visionManager.decodeFromString(data) - else -> { - window.alert("File extension is not recognized: $name") - error("File extension is not recognized: $name") - } + readAsText(file) } - vision = parsedVision as? Solid ?: error("Parsed vision is not a solid") + return deferred } child(ThreeCanvasWithControls) { attrs { this.context = props.context - this.solid = vision + this.builderOfSolid = deferredVision this.selected = props.selected tab("Load") { h2 { @@ -61,13 +75,7 @@ val GDMLApp = functionalComponent("GDMLApp") { props -> fileDrop("(drag file here)") { files -> val file = files?.get(0) if (file != null) { - FileReader().apply { - onload = { - val string = result as String - loadData(file.name, string) - } - readAsText(file) - } + deferredVision = readFileAsync(file) } } } diff --git a/demo/js-playground/src/main/kotlin/JsPlaygroundApp.kt b/demo/js-playground/src/main/kotlin/JsPlaygroundApp.kt index a54e48c9..55407e4f 100644 --- a/demo/js-playground/src/main/kotlin/JsPlaygroundApp.kt +++ b/demo/js-playground/src/main/kotlin/JsPlaygroundApp.kt @@ -1,5 +1,4 @@ import kotlinx.browser.document -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.delay import kotlinx.coroutines.isActive import kotlinx.coroutines.launch @@ -17,6 +16,7 @@ import space.kscience.visionforge.VisionClient import space.kscience.visionforge.plotly.PlotlyPlugin import space.kscience.visionforge.ring.ThreeCanvasWithControls import space.kscience.visionforge.ring.ThreeWithControlsPlugin +import space.kscience.visionforge.ring.solid import space.kscience.visionforge.solid.* import space.kscience.visionforge.startApplication import styled.css @@ -38,51 +38,6 @@ private class JsPlaygroundApp : Application { val bouncingSphereTrace = Trace() - val bouncingSphere = SolidGroup { - sphere(5.0, "ball") { - detail = 16 - color("red") - val h = 100.0 - y = h - GlobalScope.launch { - val g = 10.0 - val dt = 0.1 - var time = 0.0 - var velocity = 0.0 - while (isActive) { - delay(20) - time += dt - velocity -= g * dt - y = y.toDouble() + velocity * dt - bouncingSphereTrace.appendXY(time, y) - if (y.toDouble() <= 2.5) { - //conservation of energy - velocity = sqrt(2 * g * h) - } - } - } - } - - box(200, 5, 200, name = "floor") { - y = -2.5 - } - } - val random = Random(112233) - - val visionOfSpheres = SolidGroup { - repeat(100) { - sphere(5, name = "sphere[$it]") { - x = random.nextDouble(-300.0, 300.0) - y = random.nextDouble(-300.0, 300.0) - z = random.nextDouble(-300.0, 300.0) - material { - color(random.nextInt()) - } - detail = 16 - } - } - } - render(element) { styledDiv { css { @@ -94,18 +49,46 @@ private class JsPlaygroundApp : Application { SmartTabs("gravity") { Tab("gravity") { styledDiv { - css{ + css { height = 50.vh } child(ThreeCanvasWithControls) { attrs { context = playgroundContext - solid = bouncingSphere + solid { + sphere(5.0, "ball") { + detail = 16 + color("red") + val h = 100.0 + y = h + launch { + val g = 10.0 + val dt = 0.1 + var time = 0.0 + var velocity = 0.0 + while (isActive) { + delay(20) + time += dt + velocity -= g * dt + y = y.toDouble() + velocity * dt + bouncingSphereTrace.appendXY(time, y) + if (y.toDouble() <= 2.5) { + //conservation of energy + velocity = sqrt(2 * g * h) + } + } + } + } + + box(200, 5, 200, name = "floor") { + y = -2.5 + } + } } } } styledDiv { - css{ + css { height = 40.vh } @@ -129,10 +112,28 @@ private class JsPlaygroundApp : Application { // } // } Tab("spheres") { - child(ThreeCanvasWithControls) { - attrs { - context = playgroundContext - solid = visionOfSpheres + styledDiv { + css { + height = 90.vh + } + child(ThreeCanvasWithControls) { + val random = Random(112233) + attrs { + context = playgroundContext + solid { + repeat(100) { + sphere(5, name = "sphere[$it]") { + x = random.nextDouble(-300.0, 300.0) + y = random.nextDouble(-300.0, 300.0) + z = random.nextDouble(-300.0, 300.0) + material { + color(random.nextInt()) + } + detail = 16 + } + } + } + } } } } diff --git a/ui/ring/src/main/kotlin/ringui/Loader.kt b/ui/ring/src/main/kotlin/ringui/Loader.kt new file mode 100644 index 00000000..c58d51c1 --- /dev/null +++ b/ui/ring/src/main/kotlin/ringui/Loader.kt @@ -0,0 +1,19 @@ +@file:JsModule("@jetbrains/ring-ui/components/loader/loader") +@file:JsNonModule + +package ringui + +import react.ComponentClass +import react.dom.WithClassName + +// https://github.com/JetBrains/ring-ui/blob/master/components/loader/loader.js +public external interface LoaderProps : WithClassName { + public var size: Number + public var colors: Array + public var message: String + public var stop: Boolean + public var deterministic: Boolean +} + +@JsName("default") +public external val Loader: ComponentClass \ No newline at end of file diff --git a/ui/ring/src/main/kotlin/ringui/LoaderScreen.kt b/ui/ring/src/main/kotlin/ringui/LoaderScreen.kt new file mode 100644 index 00000000..8d0bf578 --- /dev/null +++ b/ui/ring/src/main/kotlin/ringui/LoaderScreen.kt @@ -0,0 +1,16 @@ +@file:JsModule("@jetbrains/ring-ui/components/loader-screen/loader-screen") +@file:JsNonModule + +package ringui + +import react.ComponentClass +import react.dom.WithClassName + +// https://github.com/JetBrains/ring-ui/blob/master/components/loader-screen/loader-screen.js +public external interface LoaderScreenProps : WithClassName { + public var containerClassName: String + public var message: String +} + +@JsName("default") +public external val LoaderScreen: ComponentClass \ No newline at end of file diff --git a/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ThreeViewWithControls.kt b/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ThreeViewWithControls.kt index a14d23ca..06cffdd0 100644 --- a/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ThreeViewWithControls.kt +++ b/ui/ring/src/main/kotlin/space.kscience.visionforge.ring/ThreeViewWithControls.kt @@ -1,18 +1,19 @@ package space.kscience.visionforge.ring +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.async +import kotlinx.coroutines.launch import kotlinx.css.* import react.* import react.dom.div import react.dom.span -import ringui.Island -import ringui.IslandContent -import ringui.IslandHeader -import ringui.Link +import ringui.* import space.kscience.dataforge.context.Context import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.NameToken import space.kscience.dataforge.names.isEmpty import space.kscience.dataforge.names.length +import space.kscience.visionforge.Vision import space.kscience.visionforge.VisionGroup import space.kscience.visionforge.computeProperties import space.kscience.visionforge.react.ThreeCanvasComponent @@ -20,17 +21,24 @@ 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.SolidGroup import space.kscience.visionforge.solid.specifications.Canvas3DOptions import styled.css import styled.styledDiv public external interface ThreeCanvasWithControlsProps : RProps { public var context: Context - public var solid: Solid? + public var builderOfSolid: Deferred public var selected: Name? public var additionalTabs: Map Unit>? } +public fun ThreeCanvasWithControlsProps.solid(block: SolidGroup.() -> Unit) { + builderOfSolid = context.async { + SolidGroup(block) + } +} + public fun ThreeCanvasWithControlsProps.tab(title: String, block: RBuilder.() -> Unit) { additionalTabs = (additionalTabs ?: emptyMap()) + (title to block) } @@ -72,6 +80,13 @@ public fun RBuilder.nameCrumbs(name: Name?, link: (Name) -> Unit): ReactElement public val ThreeCanvasWithControls: FunctionComponent = functionalComponent("ThreeViewWithControls") { props -> var selected by useState { props.selected } + var solid: Solid? by useState(null) + + useEffect { + props.context.launch { + solid = props.builderOfSolid.await() + } + } val onSelect: (Name?) -> Unit = { selected = it @@ -83,15 +98,16 @@ public val ThreeCanvasWithControls: FunctionComponent props.solid - else -> (props.solid as? VisionGroup)?.get(it) + it.isEmpty() -> solid + else -> (solid as? VisionGroup)?.get(it) } } } + flexRow { css { height = 100.pct @@ -108,12 +124,20 @@ public val ThreeCanvasWithControls: FunctionComponent