diff --git a/.gitignore b/.gitignore index 5fa56866..7287ad5a 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,4 @@ */build/** -!gradle-wrapper.jar - -artifactory.gradle \ No newline at end of file +!gradle-wrapper.jar \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 6b033c87..7b8168f8 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,19 +1,25 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension buildscript { - val kotlinVersion: String by rootProject.extra("1.3.20") - val ioVersion: String by rootProject.extra("0.1.4") + val kotlinVersion: String by rootProject.extra("1.3.21") + val ioVersion: String by rootProject.extra("0.1.5") val coroutinesVersion: String by rootProject.extra("1.1.1") - val serializationVersion: String by rootProject.extra("0.9.1") + val atomicfuVersion: String by rootProject.extra("0.12.1") + val dokkaVersion: String by rootProject.extra("0.9.17") + val serializationVersion: String by rootProject.extra("0.10.0") repositories { jcenter() + maven("https://dl.bintray.com/kotlin/kotlin-eap") } dependencies { classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion") - classpath("org.jetbrains.kotlin:kotlin-serialization:$kotlinVersion") classpath("org.jfrog.buildinfo:build-info-extractor-gradle:4+") + classpath("com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4") + classpath("org.jetbrains.dokka:dokka-gradle-plugin:$dokkaVersion") + classpath("org.jetbrains.kotlin:kotlin-frontend-plugin:0.0.45") + classpath("org.openjfx:javafx-plugin:0.0.7") } } @@ -23,6 +29,7 @@ plugins { } allprojects { + apply(plugin = "maven") apply(plugin = "maven-publish") apply(plugin = "com.jfrog.artifactory") @@ -32,8 +39,17 @@ allprojects { } group = "hep.dataforge" - version = "0.1.1-dev-3" + version = "0.1.1-dev-4" + // apply bintray configuration + apply(from = "${rootProject.rootDir}/gradle/bintray.gradle") + + //apply artifactory configuration + apply(from = "${rootProject.rootDir}/gradle/artifactory.gradle") + +} + +subprojects { extensions.findByType()?.apply { jvm { compilations.all { @@ -48,8 +64,61 @@ allprojects { } } } + + // dokka { +// outputFormat = "html" +// outputDirectory = javadoc.destinationDir +// } +// +// task dokkaJar (type: Jar, dependsOn: dokka) { +// from javadoc . destinationDir +// classifier = "javadoc" +// } + + + if (!name.startsWith("dataforge")) return@subprojects + + extensions.findByType()?.apply { + publications.filterIsInstance().forEach { publication -> + if (publication.name == "kotlinMultiplatform") { + // for our root metadata publication, set artifactId with a package and project name + publication.artifactId = project.name + } else { + // for targets, set artifactId with a package, project name and target name (e.g. iosX64) + publication.artifactId = "${project.name}-${publication.name}" + } + } + + // Create empty jar for sources classifier to satisfy maven requirements + val stubSources by tasks.registering(Jar::class) { + archiveClassifier.set("sources") + //from(sourceSets.main.get().allSource) + } + + // Create empty jar for javadoc classifier to satisfy maven requirements + val stubJavadoc by tasks.registering(Jar::class) { + archiveClassifier.set("javadoc") + } + + extensions.findByType()?.apply { + + targets.forEach { target -> + val publication = publications.findByName(target.name) as MavenPublication + + // Patch publications with fake javadoc + publication.artifact(stubJavadoc) + + // Remove gradle metadata publishing from all targets which are not native +// if (target.platformType.name != "native") { +// publication.gradleModuleMetadataFile = null +// tasks.matching { it.name == "generateMetadataFileFor${name.capitalize()}Publication" }.all { +// onlyIf { false } +// } +// } + } + } + } + } -if (file("artifactory.gradle").exists()) { - apply(from = "artifactory.gradle") -} \ No newline at end of file + diff --git a/dataforge-io/build.gradle.kts b/dataforge-io/build.gradle.kts index e0ae01c6..1756f10d 100644 --- a/dataforge-io/build.gradle.kts +++ b/dataforge-io/build.gradle.kts @@ -2,10 +2,6 @@ plugins { kotlin("multiplatform") } -repositories { - jcenter() -} - kotlin { jvm() js() diff --git a/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/Binary.kt b/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/Binary.kt new file mode 100644 index 00000000..7ab032c2 --- /dev/null +++ b/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/Binary.kt @@ -0,0 +1,7 @@ +package hep.dataforge.meta.io + +import kotlinx.io.ByteBuffer +import kotlinx.io.core.Input + +//TODO replace by abstraction +typealias Binary = Input \ No newline at end of file diff --git a/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/Envelope.kt b/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/Envelope.kt new file mode 100644 index 00000000..ce01d506 --- /dev/null +++ b/dataforge-meta-io/src/commonMain/kotlin/hep/dataforge/meta/io/Envelope.kt @@ -0,0 +1,50 @@ +package hep.dataforge.meta.io + +import hep.dataforge.meta.Meta +import hep.dataforge.meta.get +import hep.dataforge.meta.string + +interface Envelope { + val meta: Meta + val data: Binary? + + companion object { +// /** +// * Property keys +// */ +// const val TYPE_PROPERTY = "type" +// const val META_TYPE_PROPERTY = "metaType" +// const val META_LENGTH_PROPERTY = "metaLength" +// const val DATA_LENGTH_PROPERTY = "dataLength" + + /** + * meta keys + */ + const val ENVELOPE_NODE = "@envelope" + const val ENVELOPE_TYPE_KEY = "$ENVELOPE_NODE.type" + const val ENVELOPE_DATA_TYPE_KEY = "$ENVELOPE_NODE.dataType" + const val ENVELOPE_DESCRIPTION_KEY = "$ENVELOPE_NODE.description" + //const val ENVELOPE_TIME_KEY = "@envelope.time" + } +} + +/** + * The purpose of the envelope + * + * @return + */ +val Envelope.type: String? get() = meta[Envelope.ENVELOPE_TYPE_KEY].string + +/** + * The type of data encoding + * + * @return + */ +val Envelope.dataType: String? get() = meta[Envelope.ENVELOPE_DATA_TYPE_KEY].string + +/** + * Textual user friendly description + * + * @return + */ +val Envelope.description: String? get() = meta[Envelope.ENVELOPE_DESCRIPTION_KEY].string \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Config.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Config.kt index 4a5d23fe..79407337 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Config.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Config.kt @@ -1,6 +1,7 @@ package hep.dataforge.meta import hep.dataforge.names.Name +import hep.dataforge.names.NameToken import hep.dataforge.names.toName //TODO add validator to configuration @@ -16,8 +17,14 @@ open class Config : MutableMetaNode() { override fun wrap(name: Name, meta: Meta): Config = meta.toConfig() override fun empty(): Config = Config() + + companion object { + fun empty(): Config = Config() + } } +operator fun Config.get(token: NameToken): MetaItem? = items[token] + fun Meta.toConfig(): Config = this as? Config ?: Config().also { builder -> this.items.mapValues { entry -> val item = entry.value diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Delegates.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Delegates.kt index fadfc673..5672d2cf 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Delegates.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Delegates.kt @@ -2,6 +2,7 @@ package hep.dataforge.meta import hep.dataforge.values.Null import hep.dataforge.values.Value +import hep.dataforge.values.asValue import kotlin.jvm.JvmName import kotlin.properties.ReadOnlyProperty import kotlin.properties.ReadWriteProperty @@ -11,247 +12,348 @@ import kotlin.reflect.KProperty //TODO add caching for sealed nodes -class ValueDelegate(private val key: String? = null, private val default: Value? = null) : - ReadOnlyProperty { - override fun getValue(thisRef: Metoid, property: KProperty<*>): Value? { - return thisRef.meta[key ?: property.name]?.value ?: default +class ValueDelegate(val meta: Meta, private val key: String? = null, private val default: Value? = null) : + ReadOnlyProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): Value? { + return meta[key ?: property.name]?.value ?: default } } -class StringDelegate(private val key: String? = null, private val default: String? = null) : - ReadOnlyProperty { - override fun getValue(thisRef: Metoid, property: KProperty<*>): String? { - return thisRef.meta[key ?: property.name]?.string ?: default +class StringDelegate(val meta: Meta, private val key: String? = null, private val default: String? = null) : + ReadOnlyProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): String? { + return meta[key ?: property.name]?.string ?: default } } -class BooleanDelegate(private val key: String? = null, private val default: Boolean? = null) : +class BooleanDelegate(val meta: Meta, private val key: String? = null, private val default: Boolean? = null) : ReadOnlyProperty { override fun getValue(thisRef: Metoid, property: KProperty<*>): Boolean? { - return thisRef.meta[key ?: property.name]?.boolean ?: default + return meta[key ?: property.name]?.boolean ?: default } } -class NumberDelegate(private val key: String? = null, private val default: Number? = null) : - ReadOnlyProperty { - override fun getValue(thisRef: Metoid, property: KProperty<*>): Number? { - return thisRef.meta[key ?: property.name]?.number ?: default +class NumberDelegate(val meta: Meta, private val key: String? = null, private val default: Number? = null) : + ReadOnlyProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): Number? { + return meta[key ?: property.name]?.number ?: default + } + + //delegates for number transformation + + val double get() = DelegateWrapper(this) { it?.toDouble() } + val int get() = DelegateWrapper(this) { it?.toInt() } + val short get() = DelegateWrapper(this) { it?.toShort() } + val long get() = DelegateWrapper(this) { it?.toLong() } +} + +class DelegateWrapper(val delegate: ReadOnlyProperty, val reader: (T) -> R) : + ReadOnlyProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): R { + return reader(delegate.getValue(thisRef, property)) } } //Delegates with non-null values -class SafeStringDelegate(private val key: String? = null, private val default: String) : - ReadOnlyProperty { - override fun getValue(thisRef: Metoid, property: KProperty<*>): String { - return thisRef.meta[key ?: property.name]?.string ?: default +class SafeStringDelegate(val meta: Meta, private val key: String? = null, private val default: String) : + ReadOnlyProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): String { + return meta[key ?: property.name]?.string ?: default } } -class SafeBooleanDelegate(private val key: String? = null, private val default: Boolean) : - ReadOnlyProperty { - override fun getValue(thisRef: Metoid, property: KProperty<*>): Boolean { - return thisRef.meta[key ?: property.name]?.boolean ?: default +class SafeBooleanDelegate(val meta: Meta, private val key: String? = null, private val default: Boolean) : + ReadOnlyProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): Boolean { + return meta[key ?: property.name]?.boolean ?: default } } -class SafeNumberDelegate(private val key: String? = null, private val default: Number) : - ReadOnlyProperty { - override fun getValue(thisRef: Metoid, property: KProperty<*>): Number { - return thisRef.meta[key ?: property.name]?.number ?: default +class SafeNumberDelegate(val meta: Meta, private val key: String? = null, private val default: Number) : + ReadOnlyProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): Number { + return meta[key ?: property.name]?.number ?: default } + + val double get() = DelegateWrapper(this) { it.toDouble() } + val int get() = DelegateWrapper(this) { it.toInt() } + val short get() = DelegateWrapper(this) { it.toShort() } + val long get() = DelegateWrapper(this) { it.toLong() } } class SafeEnumDelegate>( + val meta: Meta, private val key: String? = null, private val default: E, private val resolver: (String) -> E -) : ReadOnlyProperty { - override fun getValue(thisRef: Metoid, property: KProperty<*>): E { - return (thisRef.meta[key ?: property.name]?.string)?.let { resolver(it) } ?: default +) : ReadOnlyProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): E { + return (meta[key ?: property.name]?.string)?.let { resolver(it) } ?: default } } //Child node delegate -class ChildDelegate(private val key: String? = null, private val converter: (Meta) -> T) : - ReadOnlyProperty { - override fun getValue(thisRef: Metoid, property: KProperty<*>): T? { - return thisRef.meta[key ?: property.name]?.node?.let { converter(it) } +class ChildDelegate(val meta: Meta, private val key: String? = null, private val converter: (Meta) -> T) : + ReadOnlyProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): T? { + return meta[key ?: property.name]?.node?.let { converter(it) } } } -//Read-only delegates +//Read-only delegates for Metas /** * A property delegate that uses custom key */ -fun Metoid.value(default: Value = Null, key: String? = null) = ValueDelegate(key, default) +fun Meta.value(default: Value = Null, key: String? = null) = ValueDelegate(this, key, default) -fun Metoid.string(default: String? = null, key: String? = null) = StringDelegate(key, default) +fun Meta.string(default: String? = null, key: String? = null) = StringDelegate(this, key, default) -fun Metoid.boolean(default: Boolean? = null, key: String? = null) = BooleanDelegate(key, default) +fun Meta.boolean(default: Boolean? = null, key: String? = null) = BooleanDelegate(this, key, default) -fun Metoid.number(default: Number? = null, key: String? = null) = NumberDelegate(key, default) +fun Meta.number(default: Number? = null, key: String? = null) = NumberDelegate(this, key, default) -fun Metoid.child(key: String? = null) = ChildDelegate(key) { it } - -fun Metoid.child(key: String? = null, converter: (Meta) -> T) = ChildDelegate(key, converter) +fun Meta.child(key: String? = null) = ChildDelegate(this, key) { it } @JvmName("safeString") -fun Metoid.string(default: String, key: String? = null) = SafeStringDelegate(key, default) +fun Meta.string(default: String, key: String? = null) = SafeStringDelegate(this, key, default) @JvmName("safeBoolean") -fun Metoid.boolean(default: Boolean, key: String? = null) = SafeBooleanDelegate(key, default) +fun Meta.boolean(default: Boolean, key: String? = null) = SafeBooleanDelegate(this, key, default) @JvmName("safeNumber") -fun Metoid.number(default: Number, key: String? = null) = SafeNumberDelegate(key, default) +fun Meta.number(default: Number, key: String? = null) = SafeNumberDelegate(this, key, default) + +inline fun > Meta.enum(default: E, key: String? = null) = + SafeEnumDelegate(this, key, default) { enumValueOf(it) } -inline fun > Metoid.enum(default: E, key: String? = null) = - SafeEnumDelegate(key, default) { enumValueOf(it) } /* Config delegates */ -class ValueConfigDelegate(private val key: String? = null, private val default: Value? = null) : - ReadWriteProperty { - override fun getValue(thisRef: Configurable, property: KProperty<*>): Value? { - return thisRef.config[key ?: property.name]?.value ?: default +class ValueConfigDelegate>( + val config: M, + private val key: String? = null, + private val default: Value? = null +) : ReadWriteProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): Value? { + return config[key ?: property.name]?.value ?: default } - override fun setValue(thisRef: Configurable, property: KProperty<*>, value: Value?) { + override fun setValue(thisRef: Any?, property: KProperty<*>, value: Value?) { val name = key ?: property.name if (value == null) { - thisRef.config.remove(name) + config.remove(name) } else { - thisRef.config[name] = value + config.setValue(name, value) } } } -class StringConfigDelegate(private val key: String? = null, private val default: String? = null) : - ReadWriteProperty { - override fun getValue(thisRef: Configurable, property: KProperty<*>): String? { - return thisRef.config[key ?: property.name]?.string ?: default +class StringConfigDelegate>( + val config: M, + private val key: String? = null, + private val default: String? = null +) : ReadWriteProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): String? { + return config[key ?: property.name]?.string ?: default } - override fun setValue(thisRef: Configurable, property: KProperty<*>, value: String?) { - thisRef.config[key ?: property.name] = value + override fun setValue(thisRef: Any?, property: KProperty<*>, value: String?) { + val name = key ?: property.name + if (value == null) { + config.remove(name) + } else { + config.setValue(name, value.asValue()) + } } } -class BooleanConfigDelegate(private val key: String? = null, private val default: Boolean? = null) : - ReadWriteProperty { - override fun getValue(thisRef: Configurable, property: KProperty<*>): Boolean? { - return thisRef.config[key ?: property.name]?.boolean ?: default +class BooleanConfigDelegate>( + val config: M, + private val key: String? = null, + private val default: Boolean? = null +) : ReadWriteProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): Boolean? { + return config[key ?: property.name]?.boolean ?: default } - override fun setValue(thisRef: Configurable, property: KProperty<*>, value: Boolean?) { - thisRef.config[key ?: property.name] = value + override fun setValue(thisRef: Any?, property: KProperty<*>, value: Boolean?) { + val name = key ?: property.name + if (value == null) { + config.remove(name) + } else { + config.setValue(name, value.asValue()) + } } } -class NumberConfigDelegate(private val key: String? = null, private val default: Number? = null) : - ReadWriteProperty { - override fun getValue(thisRef: Configurable, property: KProperty<*>): Number? { - return thisRef.config[key ?: property.name]?.number ?: default +class NumberConfigDelegate>( + val config: M, + private val key: String? = null, + private val default: Number? = null +) : ReadWriteProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): Number? { + return config[key ?: property.name]?.number ?: default } - override fun setValue(thisRef: Configurable, property: KProperty<*>, value: Number?) { - thisRef.config[key ?: property.name] = value + override fun setValue(thisRef: Any?, property: KProperty<*>, value: Number?) { + val name = key ?: property.name + if (value == null) { + config.remove(name) + } else { + config.setValue(name, value.asValue()) + } } + + val double get() = ReadWriteDelegateWrapper(this, reader = { it?.toDouble() }, writer = { it }) + val int get() = ReadWriteDelegateWrapper(this, reader = { it?.toInt() }, writer = { it }) + val short get() = ReadWriteDelegateWrapper(this, reader = { it?.toShort() }, writer = { it }) + val long get() = ReadWriteDelegateWrapper(this, reader = { it?.toLong() }, writer = { it }) } //Delegates with non-null values -class SafeStringConfigDelegate(private val key: String? = null, private val default: String) : - ReadWriteProperty { - override fun getValue(thisRef: Configurable, property: KProperty<*>): String { - return thisRef.config[key ?: property.name]?.string ?: default +class SafeStringConfigDelegate>( + val config: M, + private val key: String? = null, + private val default: String +) : ReadWriteProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): String { + return config[key ?: property.name]?.string ?: default } - override fun setValue(thisRef: Configurable, property: KProperty<*>, value: String) { - thisRef.config[key ?: property.name] = value + override fun setValue(thisRef: Any?, property: KProperty<*>, value: String) { + config.setValue(key ?: property.name, value.asValue()) } } -class SafeBooleanConfigDelegate(private val key: String? = null, private val default: Boolean) : - ReadWriteProperty { - override fun getValue(thisRef: Configurable, property: KProperty<*>): Boolean { - return thisRef.config[key ?: property.name]?.boolean ?: default +class SafeBooleanConfigDelegate>( + val config: M, + private val key: String? = null, + private val default: Boolean +) : ReadWriteProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): Boolean { + return config[key ?: property.name]?.boolean ?: default } - override fun setValue(thisRef: Configurable, property: KProperty<*>, value: Boolean) { - thisRef.config[key ?: property.name] = value + override fun setValue(thisRef: Any?, property: KProperty<*>, value: Boolean) { + config.setValue(key ?: property.name, value.asValue()) } } -class SafeNumberConfigDelegate(private val key: String? = null, private val default: Number) : - ReadWriteProperty { - override fun getValue(thisRef: Configurable, property: KProperty<*>): Number { - return thisRef.config[key ?: property.name]?.number ?: default +class SafeNumberConfigDelegate>( + val config: M, + private val key: String? = null, + private val default: Number +) : ReadWriteProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): Number { + return config[key ?: property.name]?.number ?: default } - override fun setValue(thisRef: Configurable, property: KProperty<*>, value: Number) { - thisRef.config[key ?: property.name] = value + override fun setValue(thisRef: Any?, property: KProperty<*>, value: Number) { + config.setValue(key ?: property.name, value.asValue()) } + + val double get() = ReadWriteDelegateWrapper(this, reader = { it.toDouble() }, writer = { it }) + val int get() = ReadWriteDelegateWrapper(this, reader = { it.toInt() }, writer = { it }) + val short get() = ReadWriteDelegateWrapper(this, reader = { it.toShort() }, writer = { it }) + val long get() = ReadWriteDelegateWrapper(this, reader = { it.toLong() }, writer = { it }) } -class SafeEnumvConfigDelegate>( +class SafeEnumvConfigDelegate, E : Enum>( + val config: M, private val key: String? = null, private val default: E, private val resolver: (String) -> E -) : ReadWriteProperty { - override fun getValue(thisRef: Configurable, property: KProperty<*>): E { - return (thisRef.config[key ?: property.name]?.string)?.let { resolver(it) } ?: default +) : ReadWriteProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): E { + return (config[key ?: property.name]?.string)?.let { resolver(it) } ?: default } - override fun setValue(thisRef: Configurable, property: KProperty<*>, value: E) { - thisRef.config[key ?: property.name] = value.name + override fun setValue(thisRef: Any?, property: KProperty<*>, value: E) { + config.setValue(key ?: property.name, value.name.asValue()) } } //Child node delegate -class ChildConfigDelegate(private val key: String? = null, private val converter: (Config) -> T) : - ReadWriteProperty { - override fun getValue(thisRef: Configurable, property: KProperty<*>): T { - return converter(thisRef.config[key ?: property.name]?.node ?: Config()) +class MetaNodeDelegate>( + val config: M, + private val key: String? = null +) : ReadWriteProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): Meta { + return config[key ?: property.name]?.node ?: EmptyMeta } - override fun setValue(thisRef: Configurable, property: KProperty<*>, value: T) { - thisRef.config[key ?: property.name] = value.config + override fun setValue(thisRef: Any?, property: KProperty<*>, value: Meta) { + config[key ?: property.name] = value } - } +class ChildConfigDelegate, T : Configurable>( + val config: M, + private val key: String? = null, + private val converter: (Meta) -> T +) : + ReadWriteProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): T { + return converter(config[key ?: property.name]?.node ?: EmptyMeta) + } + + override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { + config[key ?: property.name] = value.config + } +} + +class ReadWriteDelegateWrapper( + val delegate: ReadWriteProperty, + val reader: (T) -> R, + val writer: (R) -> T +) : ReadWriteProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): R { + return reader(delegate.getValue(thisRef, property)) + } + + override fun setValue(thisRef: Any?, property: KProperty<*>, value: R) { + delegate.setValue(thisRef, property, writer(value)) + } +} + + //Read-write delegates /** * A property delegate that uses custom key */ -fun Configurable.value(default: Value = Null, key: String? = null) = ValueConfigDelegate(key, default) +fun > M.value(default: Value = Null, key: String? = null) = + ValueConfigDelegate(this, key, default) -fun Configurable.string(default: String? = null, key: String? = null) = StringConfigDelegate(key, default) +fun > M.string(default: String? = null, key: String? = null) = + StringConfigDelegate(this, key, default) -fun Configurable.boolean(default: Boolean? = null, key: String? = null) = BooleanConfigDelegate(key, default) +fun > M.boolean(default: Boolean? = null, key: String? = null) = + BooleanConfigDelegate(this, key, default) -fun Configurable.number(default: Number? = null, key: String? = null) = NumberConfigDelegate(key, default) +fun > M.number(default: Number? = null, key: String? = null) = + NumberConfigDelegate(this, key, default) -fun Configurable.child(key: String? = null) = ChildConfigDelegate(key) { SimpleConfigurable(it) } - -fun Configurable.child(key: String? = null, converter: (Config) -> T) = - ChildConfigDelegate(key, converter) +fun > M.child(key: String? = null) = MetaNodeDelegate(this, key) //fun Configurable.spec(spec: Specification, key: String? = null) = ChildConfigDelegate(key) { spec.wrap(this) } @JvmName("safeString") -fun Configurable.string(default: String, key: String? = null) = SafeStringConfigDelegate(key, default) +fun > M.string(default: String, key: String? = null) = + SafeStringConfigDelegate(this, key, default) @JvmName("safeBoolean") -fun Configurable.boolean(default: Boolean, key: String? = null) = SafeBooleanConfigDelegate(key, default) +fun > M.boolean(default: Boolean, key: String? = null) = + SafeBooleanConfigDelegate(this, key, default) @JvmName("safeNumber") -fun Configurable.number(default: Number, key: String? = null) = SafeNumberConfigDelegate(key, default) +fun > M.number(default: Number, key: String? = null) = + SafeNumberConfigDelegate(this, key, default) -inline fun > Configurable.enum(default: E, key: String? = null) = - SafeEnumvConfigDelegate(key, default) { enumValueOf(it) } \ No newline at end of file +inline fun , reified E : Enum> M.enum(default: E, key: String? = null) = + SafeEnumvConfigDelegate(this, key, default) { enumValueOf(it) } \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ExtraMetaDelegates.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ExtraMetaDelegates.kt new file mode 100644 index 00000000..d0eaa54e --- /dev/null +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ExtraMetaDelegates.kt @@ -0,0 +1,36 @@ +package hep.dataforge.meta + +import kotlin.properties.ReadWriteProperty +import kotlin.reflect.KProperty + +/* + * Extra delegates for special cases + */ + +/** + * A delegate for a string list + */ +class StringListConfigDelegate( + val config: Config, + private val key: String? = null, + private val default: List = emptyList() +) : + ReadWriteProperty> { + override fun getValue(thisRef: Any?, property: KProperty<*>): List { + return config[key ?: property.name]?.value?.list?.map { it.string } ?: default + } + + override fun setValue(thisRef: Any?, property: KProperty<*>, value: List) { + val name = key ?: property.name + config[name] = value + } +} + +fun Configurable.stringList(vararg default: String = emptyArray(), key: String? = null) = + StringListConfigDelegate(config, key, default.toList()) + + +fun Metoid.child(key: String? = null, converter: (Meta) -> T) = ChildDelegate(meta, key, converter) + +fun Configurable.child(key: String? = null, converter: (Meta) -> T) = + ChildConfigDelegate(config, key, converter) diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Laminate.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Laminate.kt index 094380c8..a1a62760 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Laminate.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Laminate.kt @@ -3,7 +3,7 @@ package hep.dataforge.meta import hep.dataforge.names.NameToken /** - * A meta laminate consisting of multiple immutable meta layers. For mutable front layer, use [StyledConfig]. + * A meta laminate consisting of multiple immutable meta layers. For mutable front layer, use [Styled]. * * */ diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt index 6d00d319..2a9ef237 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt @@ -102,21 +102,26 @@ operator fun Meta.iterator(): Iterator> = asValueSequence().it /** * A meta node that ensures that all of its descendants has at least the same type */ -abstract class MetaNode> : Meta { - abstract override val items: Map> +interface MetaNode> : Meta { + override val items: Map> +} - operator fun get(name: Name): MetaItem? { - return name.first()?.let { token -> - val tail = name.cutFirst() - when (tail.length) { - 0 -> items[token] - else -> items[token]?.node?.get(tail) - } +operator fun > MetaNode.get(name: Name): MetaItem? { + return name.first()?.let { token -> + val tail = name.cutFirst() + when (tail.length) { + 0 -> items[token] + else -> items[token]?.node?.get(tail) } } +} - operator fun get(key: String): MetaItem? = get(key.toName()) +operator fun > MetaNode.get(key: String): MetaItem? = get(key.toName()) +/** + * Equals and hash code implementation for meta node + */ +abstract class AbstractMetaNode> : MetaNode { override fun equals(other: Any?): Boolean { if (this === other) return true if (other !is Meta) return false @@ -134,7 +139,8 @@ abstract class MetaNode> : Meta { * * If the argument is possibly mutable node, it is copied on creation */ -class SealedMeta internal constructor(override val items: Map>) : MetaNode() +class SealedMeta internal constructor(override val items: Map>) : + AbstractMetaNode() /** * Generate sealed node from [this]. If it is already sealed return it as is @@ -154,17 +160,19 @@ object EmptyMeta : Meta { * Unsafe methods to access values and nodes directly from [MetaItem] */ -val MetaItem<*>.value +val MetaItem<*>?.value get() = (this as? MetaItem.ValueItem)?.value - ?: (this.node[VALUE_KEY] as? MetaItem.ValueItem)?.value - ?: error("Trying to interpret node meta item as value item") -val MetaItem<*>.string get() = value.string -val MetaItem<*>.boolean get() = value.boolean -val MetaItem<*>.number get() = value.number -val MetaItem<*>.double get() = number.toDouble() -val MetaItem<*>.int get() = number.toInt() -val MetaItem<*>.long get() = number.toLong() -val MetaItem<*>.short get() = number.toShort() + ?: (this?.node?.get(VALUE_KEY) as? MetaItem.ValueItem)?.value + +val MetaItem<*>?.string get() = value?.string +val MetaItem<*>?.boolean get() = value?.boolean +val MetaItem<*>?.number get() = value?.number +val MetaItem<*>?.double get() = number?.toDouble() +val MetaItem<*>?.int get() = number?.toInt() +val MetaItem<*>?.long get() = number?.toLong() +val MetaItem<*>?.short get() = number?.toShort() + +val MetaItem<*>?.stringList get() = value?.list?.map { it.string } ?: emptyList() val MetaItem.node: M get() = when (this) { diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMetaNode.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt similarity index 76% rename from dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMetaNode.kt rename to dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt index 31998b46..788758fa 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMetaNode.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt @@ -6,19 +6,17 @@ import hep.dataforge.names.plus import hep.dataforge.names.toName import hep.dataforge.values.Value -class MetaListener( +internal data class MetaListener( val owner: Any? = null, val action: (name: Name, oldItem: MetaItem<*>?, newItem: MetaItem<*>?) -> Unit -) { - operator fun invoke(name: Name, oldItem: MetaItem<*>?, newItem: MetaItem<*>?) = action(name, oldItem, newItem) -} +) -interface MutableMeta> : Meta { +interface MutableMeta> : MetaNode { override val items: Map> operator fun set(name: Name, item: MetaItem?) fun onChange(owner: Any? = null, action: (Name, MetaItem<*>?, MetaItem<*>?) -> Unit) - fun removeListener(owner: Any) + fun removeListener(owner: Any? = null) } /** @@ -26,7 +24,7 @@ interface MutableMeta> : Meta { * * Changes in Meta are not thread safe. */ -abstract class MutableMetaNode> : MetaNode(), MutableMeta { +abstract class MutableMetaNode> : AbstractMetaNode(), MutableMeta { private val listeners = HashSet() /** @@ -39,7 +37,7 @@ abstract class MutableMetaNode> : MetaNode(), MutableM /** * Remove all listeners belonging to given owner */ - override fun removeListener(owner: Any) { + override fun removeListener(owner: Any?) { listeners.removeAll { it.owner === owner } } @@ -49,7 +47,7 @@ abstract class MutableMetaNode> : MetaNode(), MutableM get() = _items protected fun itemChanged(name: Name, oldItem: MetaItem<*>?, newItem: MetaItem<*>?) { - listeners.forEach { it(name, oldItem, newItem) } + listeners.forEach { it.action(name, oldItem, newItem) } } protected open fun replaceItem(key: NameToken, oldItem: MetaItem?, newItem: MetaItem?) { @@ -72,12 +70,12 @@ abstract class MutableMetaNode> : MetaNode(), MutableM * @param name the name of the node where meta should be attached. Needed for correct assignment validators and styles * @param meta the node itself */ - abstract fun wrap(name: Name, meta: Meta): M + internal abstract fun wrap(name: Name, meta: Meta): M /** * Create empty node */ - abstract fun empty(): M + internal abstract fun empty(): M override operator fun set(name: Name, item: MetaItem?) { when (name.length) { @@ -95,20 +93,22 @@ abstract class MutableMetaNode> : MetaNode(), MutableM } } } - - } -fun > M.remove(name: Name) = set(name, null) -fun > M.remove(name: String) = remove(name.toName()) +fun > MutableMeta.remove(name: Name) = set(name, null) +fun > MutableMeta.remove(name: String) = remove(name.toName()) -fun > M.setValue(name: Name, value: Value) = set(name, MetaItem.ValueItem(value)) -fun > M.setItem(name: String, item: MetaItem) = set(name.toName(), item) -fun > M.setValue(name: String, value: Value) = set(name.toName(), MetaItem.ValueItem(value)) -fun > M.setItem(token: NameToken, item: MetaItem?) = set(token.toName(), item) +fun > MutableMeta.setValue(name: Name, value: Value) = set(name, MetaItem.ValueItem(value)) +fun > MutableMeta.setItem(name: String, item: MetaItem) = set(name.toName(), item) +fun > MutableMeta.setValue(name: String, value: Value) = + set(name.toName(), MetaItem.ValueItem(value)) -fun > M.setNode(name: Name, node: Meta) = set(name, MetaItem.NodeItem(wrap(name, node))) -fun > M.setNode(name: String, node: Meta) = setNode(name.toName(), node) +fun > MutableMeta.setItem(token: NameToken, item: MetaItem?) = set(token.toName(), item) + +fun > MutableMetaNode.setNode(name: Name, node: Meta) = + set(name, MetaItem.NodeItem(wrap(name, node))) + +fun > MutableMetaNode.setNode(name: String, node: Meta) = setNode(name.toName(), node) /** * Universal set method @@ -116,6 +116,10 @@ fun > M.setNode(name: String, node: Meta) = setNode(name. operator fun > M.set(name: Name, value: Any?) { when (value) { null -> remove(name) + is MetaItem<*> -> when (value) { + is MetaItem.ValueItem<*> -> setValue(name, value.value) + is MetaItem.NodeItem<*> -> setNode(name, value.node) + } is Meta -> setNode(name, value) else -> setValue(name, Value.of(value)) } diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Specification.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Specification.kt index fb3e909f..f1e2beaf 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Specification.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Specification.kt @@ -55,5 +55,8 @@ fun > S.createStyle(action: C.( Config().also { update(it, action) } -fun Specification.spec(spec: SpecificationCompanion, key: String? = null) = - ChildConfigDelegate(key) { spec.wrap(config) } \ No newline at end of file +fun , C : Specification> Specification.spec( + spec: SpecificationCompanion, + key: String? = null +) = + ChildConfigDelegate(config, key) { spec.wrap(config) } \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Styleable.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Styleable.kt deleted file mode 100644 index d8aaf24e..00000000 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Styleable.kt +++ /dev/null @@ -1,68 +0,0 @@ -package hep.dataforge.meta - -import hep.dataforge.names.Name -import hep.dataforge.names.NameToken -import hep.dataforge.names.toName - -/** - * A configuration decorator with applied style - */ -class StyledConfig(val config: Config, style: Meta = EmptyMeta) : Config() { - - var style: Meta = style - set(value) { - field.items.forEach { - itemChanged(it.key.toName(), it.value, null) - } - field = value - value.items.forEach { - itemChanged(it.key.toName(), null, it.value) - } - } - - init { - config.onChange { name, oldItem, newItem -> this.itemChanged(name, oldItem, newItem) } - } - - override fun set(name: Name, item: MetaItem?) { - when (item) { - null -> config.remove(name) - is MetaItem.ValueItem -> config.setValue(name, item.value) - is MetaItem.NodeItem -> config.setNode(name, item.node) - } - } - - override val items: Map> - get() = (config.items.keys + style.items.keys).associate { key -> - val value = config.items[key] - val styleValue = style[key] - val item: MetaItem = when (value) { - null -> when (styleValue) { - null -> error("Should be unreachable") - is MetaItem.ValueItem -> MetaItem.ValueItem(styleValue.value) - is MetaItem.NodeItem -> MetaItem.NodeItem(StyledConfig(config.empty(), styleValue.node)) - } - is MetaItem.ValueItem -> MetaItem.ValueItem(value.value) - is MetaItem.NodeItem -> MetaItem.NodeItem( - StyledConfig(value.node, styleValue?.node ?: EmptyMeta) - ) - } - key to item - } -} - -fun Config.withStyle(style: Meta = EmptyMeta) = if (this is StyledConfig) { - StyledConfig(this.config, style) -} else { - StyledConfig(this, style) -} - -interface Styleable : Configurable { - override val config: StyledConfig - - var style - get() = config.style - set(value) { - config.style = value - } -} \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Styled.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Styled.kt new file mode 100644 index 00000000..aed8c413 --- /dev/null +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Styled.kt @@ -0,0 +1,68 @@ +package hep.dataforge.meta + +import hep.dataforge.names.Name +import hep.dataforge.names.NameToken +import kotlin.properties.ReadWriteProperty +import kotlin.reflect.KProperty + + +/** + * A meta object with read-only meta base and changeable configuration on top of it + * @param base - unchangeable base + * @param style - the style + */ +class Styled(val base: Meta, val style: Config = Config().empty()) : MutableMeta { + override val items: Map> + get() = (base.items.keys + style.items.keys).associate { key -> + val value = base.items[key] + val styleValue = style[key] + val item: MetaItem = when (value) { + null -> when (styleValue) { + null -> error("Should be unreachable") + is MetaItem.ValueItem -> MetaItem.ValueItem(styleValue.value) + is MetaItem.NodeItem -> MetaItem.NodeItem(Styled(style.empty(), styleValue.node)) + } + is MetaItem.ValueItem -> MetaItem.ValueItem(value.value) + is MetaItem.NodeItem -> MetaItem.NodeItem( + Styled(value.node, styleValue?.node ?: Config.empty()) + ) + } + key to item + } + + override fun set(name: Name, item: MetaItem?) { + if (item == null) { + style.remove(name) + } else { + style.set(name, item) + } + } + + override fun onChange(owner: Any?, action: (Name, before: MetaItem<*>?, after: MetaItem<*>?) -> Unit) { + //TODO test correct behavior + style.onChange(owner) { name, before, after -> action(name, before ?: base[name], after ?: base[name]) } + } + + override fun removeListener(owner: Any?) { + style.removeListener(owner) + } +} + +fun Styled.configure(meta: Meta) = apply { style.update(style) } + +fun Meta.withStyle(style: Meta = EmptyMeta) = if (this is Styled) { + this.apply { this.configure(style) } +} else { + Styled(this, style.toConfig()) +} + +class StyledNodeDelegate(val owner: Styled, val key: String?) : ReadWriteProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): Meta { + return owner[key ?: property.name]?.node ?: EmptyMeta + } + + override fun setValue(thisRef: Any?, property: KProperty<*>, value: Meta) { + owner.style[key ?: property.name] = value + } + +} \ No newline at end of file diff --git a/dataforge-vis/build.gradle.kts b/dataforge-vis/build.gradle.kts new file mode 100644 index 00000000..9ef8235b --- /dev/null +++ b/dataforge-vis/build.gradle.kts @@ -0,0 +1,24 @@ +plugins { + kotlin("multiplatform") +} + +kotlin { + jvm() + js() + + sourceSets { + val commonMain by getting { + dependencies { + api(project(":dataforge-io")) + } + } + val jvmMain by getting { + dependencies { + } + } + val jsMain by getting { + dependencies { + } + } + } +} \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-fx/build.gradle.kts b/dataforge-vis/dataforge-vis-fx/build.gradle.kts new file mode 100644 index 00000000..260b8622 --- /dev/null +++ b/dataforge-vis/dataforge-vis-fx/build.gradle.kts @@ -0,0 +1,23 @@ +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile +import org.openjfx.gradle.JavaFXOptions + +plugins { + kotlin("jvm") + id("org.openjfx.javafxplugin") +} + +dependencies{ + api(project(":dataforge-vis:dataforge-vis-spatial")) + api("no.tornado:tornadofx:1.7.18") + implementation("org.fxyz3d:fxyz3d:0.4.0") +} + +extensions.findByType()?.apply { + modules("javafx.controls") +} + +tasks.withType { + kotlinOptions{ + jvmTarget = "1.8" + } +} \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/spatial/FXSpatialRenderer.kt b/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/spatial/FXSpatialRenderer.kt new file mode 100644 index 00000000..5cd9c140 --- /dev/null +++ b/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/spatial/FXSpatialRenderer.kt @@ -0,0 +1,45 @@ +package hep.dataforge.vis.spatial + +import hep.dataforge.context.Context +import hep.dataforge.io.Output +import hep.dataforge.meta.Meta +import javafx.scene.* +import javafx.scene.paint.Color +import org.fxyz3d.geometry.Point3D +import org.fxyz3d.shapes.primitives.CuboidMesh +import org.fxyz3d.utils.CameraTransformer + +class FXSpatialRenderer(override val context: Context) : Output { + + private val world: Group = Group() + + val camera = PerspectiveCamera() + + val cameraTransform = CameraTransformer().apply { + children.add(camera) + } + + val canvas: SubScene = SubScene( + Group(world, cameraTransform).apply { DepthTest.ENABLE }, + 1024.0, + 768.0, + true, + SceneAntialiasing.BALANCED + ).apply { + fill = Color.GREY + this.camera = this@FXSpatialRenderer.camera + id = "canvas" + } + + private fun buildObject(obj: DisplayObject3D): Node { + val center = Point3D(obj.x.toFloat(), obj.y.toFloat(), obj.z.toFloat()) + return when (obj) { + is Box3D -> CuboidMesh(obj.xSize, obj.ySize, obj.zSize).apply { this.center = center } + else -> TODO() + } + } + + override fun render(obj: DisplayObject3D, meta: Meta) { + world.children.add(buildObject(obj)) + } +} \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/spatial/RendererDemoApp.kt b/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/spatial/RendererDemoApp.kt new file mode 100644 index 00000000..8ff1cc68 --- /dev/null +++ b/dataforge-vis/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/spatial/RendererDemoApp.kt @@ -0,0 +1,44 @@ +package hep.dataforge.vis.spatial + +import hep.dataforge.context.Global +import hep.dataforge.meta.EmptyMeta +import javafx.scene.Parent +import tornadofx.* + + +class RendererDemoApp: App(RendererDemoView::class) + + +class RendererDemoView: View(){ + val renderer = FXSpatialRenderer(Global) + override val root: Parent = borderpane{ + center = renderer.canvas + } + + init { + val cube = Box3D(null, EmptyMeta).apply { + xSize = 100.0 + ySize = 100.0 + zSize = 100.0 + } + renderer.render(cube) + + renderer.camera.apply { + nearClip = 0.1 + farClip = 10000.0 + translateX = -200.0 + translateY = -200.0 + fieldOfView = 20.0 + } + + renderer.cameraTransform.apply{ + ry.angle = -30.0 + rx.angle = -15.0 + } + } +} + + +fun main() { + launch() +} \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-js/build.gradle.kts b/dataforge-vis/dataforge-vis-js/build.gradle.kts new file mode 100644 index 00000000..f5e385a6 --- /dev/null +++ b/dataforge-vis/dataforge-vis-js/build.gradle.kts @@ -0,0 +1,42 @@ +plugins{ + kotlin("js") + id("kotlin") +} + +// configure(listOf(compilations.main, compilations.test)) { +// tasks.getByName(compileKotlinTaskName).kotlinOptions { +// sourceMap = true +// moduleKind = "umd" +// metaInfo = true +// } +// } +// +// configure(compilations.main) { +// tasks.getByName(compileKotlinTaskName).kotlinOptions { +// main = "call" +// } +// } + +dependencies { + implementation("info.laht.threekt:threejs-wrapper:0.88-npm-1") +} + +extensions.findByType()?.apply { + extensions.findByType()?.apply { + dependency("three") + dependency("three-orbitcontrols") + devDependency("karma") + + } + + sourceMaps = true + + bundle("webpack") { + this as WebPackExtension + bundleName = "main" + proxyUrl = "http://localhost:8080" + contentPath = file("src/main/web") + sourceMapEnabled = true + mode = "development" + } +} \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-spatial/build.gradle.kts b/dataforge-vis/dataforge-vis-spatial/build.gradle.kts new file mode 100644 index 00000000..c76b3f57 --- /dev/null +++ b/dataforge-vis/dataforge-vis-spatial/build.gradle.kts @@ -0,0 +1,26 @@ +plugins { + kotlin("multiplatform") +} + +kotlin { + jvm() + js() + + sourceSets { + val commonMain by getting { + dependencies { + api(project(":dataforge-vis")) + } + } + val jvmMain by getting { + dependencies { + + } + } + val jsMain by getting { + dependencies { + + } + } + } +} \ No newline at end of file diff --git a/dataforge-vis/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/DisplayObject3D.kt b/dataforge-vis/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/DisplayObject3D.kt new file mode 100644 index 00000000..a1d81c81 --- /dev/null +++ b/dataforge-vis/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/DisplayObject3D.kt @@ -0,0 +1,28 @@ +package hep.dataforge.vis.spatial + +import hep.dataforge.meta.Meta +import hep.dataforge.vis.DisplayLeaf +import hep.dataforge.vis.DisplayObject +import hep.dataforge.vis.double + + +open class DisplayObject3D(parent: DisplayObject?, type: String, meta: Meta) : DisplayLeaf(parent, type, meta) { + var x by double(0.0) + var y by double(0.0) + var z by double(0.0) + + companion object { + const val TYPE = "geometry.spatial" + } +} + +class Box3D(parent: DisplayObject?, meta: Meta) : DisplayObject3D(parent, + TYPE, meta) { + var xSize by double(1.0) + var ySize by double(1.0) + var zSize by double(1.0) + + companion object { + const val TYPE = "geometry.spatial.box" + } +} diff --git a/dataforge-vis/src/commonMain/kotlin/hep/dataforge/vis/DisplayObject.kt b/dataforge-vis/src/commonMain/kotlin/hep/dataforge/vis/DisplayObject.kt new file mode 100644 index 00000000..53d97074 --- /dev/null +++ b/dataforge-vis/src/commonMain/kotlin/hep/dataforge/vis/DisplayObject.kt @@ -0,0 +1,143 @@ +package hep.dataforge.vis + +import hep.dataforge.meta.* +import hep.dataforge.names.Name +import hep.dataforge.vis.DisplayObject.Companion.DEFAULT_TYPE +import hep.dataforge.vis.DisplayObject.Companion.META_KEY +import hep.dataforge.vis.DisplayObject.Companion.TAGS_KEY + +/** + * A root type for display hierarchy + */ +interface DisplayObject { + + /** + * The parent object of this one. If null, this one is a root. + */ + val parent: DisplayObject? + + /** + * The type of this object. Uses `.` notation. Empty type means untyped group + */ + val type: String + + val properties: Styled + + companion object { + const val DEFAULT_TYPE = "" + const val TYPE_KEY = "@type" + const val CHILDREN_KEY = "@children" + const val META_KEY = "@meta" + const val TAGS_KEY = "@tags" + } +} + +interface DisplayGroup : DisplayObject { + + val children: List + + /** + * Add a child object and notify listeners + */ + fun addChild(obj: DisplayObject) + + /** + * Remove a specific child and notify listeners + */ + fun removeChild(obj: DisplayObject) + + /** + * Add listener for children change + * TODO add detailed information into change listener + */ + fun onChildrenChange(owner: Any? = null, action: () -> Unit) + + /** + * Remove children change listener + */ + fun removeChildrenChangeListener(owner: Any? = null) +} + +/** + * Get the property of this display object of parent's if not found + */ +tailrec fun DisplayObject.getProperty(name: Name): MetaItem<*>? = properties[name] ?: parent?.getProperty(name) + +/** + * A change listener for [DisplayObject] configuration. + */ +fun DisplayObject.onChange(owner: Any?, action: (Name, before: MetaItem<*>?, after: MetaItem<*>?) -> Unit) = + properties.style.onChange(owner, action) + +/** + * Remove all meta listeners with matching owners + */ +fun DisplayObject.removeChangeListener(owner: Any?) = + properties.style.removeListener(owner) + + +/** + * Additional meta not relevant to display + */ +val DisplayObject.meta: Meta get() = properties[META_KEY]?.node ?: EmptyMeta + +val DisplayObject.tags: List get() = properties[TAGS_KEY].stringList + +internal data class ObjectListener( + val owner: Any?, + val action: () -> Unit +) + +/** + * Basic group of display objects + */ +open class DisplayNode( + override val parent: DisplayObject?, + override val type: String = DEFAULT_TYPE, + meta: Meta = EmptyMeta +) : DisplayGroup { + + private val _children = ArrayList() + override val children: List get() = _children + override val properties = Styled(meta) + private val listeners = HashSet() + + override fun addChild(obj: DisplayObject) { +// val before = _children[name] +// if (obj == null) { +// _children.remove(name) +// } else { +// _children[name] = obj +// } +// listeners.forEach { it.action(name, before, obj) } + _children.add(obj) + listeners.forEach { it.action() } + } + + override fun removeChild(obj: DisplayObject) { + if(_children.remove(obj)){ + listeners.forEach { it.action } + } + } + + override fun onChildrenChange(owner: Any?, action: () -> Unit) { + listeners.add(ObjectListener(owner, action)) + } + + + override fun removeChildrenChangeListener(owner: Any?) { + listeners.removeAll { it.owner === owner } + } +} + +/** + * Basic [DisplayObject] leaf element + */ +open class DisplayLeaf( + override val parent: DisplayObject?, + override val type: String, + meta: Meta = EmptyMeta +) : DisplayObject { + final override val properties = Styled(meta) +} + diff --git a/dataforge-vis/src/commonMain/kotlin/hep/dataforge/vis/DisplayObjectDelegates.kt b/dataforge-vis/src/commonMain/kotlin/hep/dataforge/vis/DisplayObjectDelegates.kt new file mode 100644 index 00000000..b3a2e042 --- /dev/null +++ b/dataforge-vis/src/commonMain/kotlin/hep/dataforge/vis/DisplayObjectDelegates.kt @@ -0,0 +1,48 @@ +package hep.dataforge.vis + +import hep.dataforge.meta.* +import hep.dataforge.values.Null +import hep.dataforge.values.Value +import kotlin.jvm.JvmName + +fun DisplayObject.value(default: Value = Null, key: String? = null) = + ValueConfigDelegate(properties, key, default) + +fun DisplayObject.string(default: String? = null, key: String? = null) = + StringConfigDelegate(properties, key, default) + +fun DisplayObject.boolean(default: Boolean? = null, key: String? = null) = + BooleanConfigDelegate(properties, key, default) + +fun DisplayObject.number(default: Number? = null, key: String? = null) = + NumberConfigDelegate(properties, key, default) + +fun DisplayObject.double(default: Double? = null, key: String? = null) = + NumberConfigDelegate(properties, key, default).double + +fun DisplayObject.int(default: Int? = null, key: String? = null) = + NumberConfigDelegate(properties, key, default).int + + +fun DisplayObject.node(key: String? = null) = StyledNodeDelegate(properties, key) + +//fun Configurable.spec(spec: Specification, key: String? = null) = ChildConfigDelegate(key) { spec.wrap(this) } + +@JvmName("safeString") +fun DisplayObject.string(default: String, key: String? = null) = + SafeStringConfigDelegate(properties, key, default) + +@JvmName("safeBoolean") +fun DisplayObject.boolean(default: Boolean, key: String? = null) = + SafeBooleanConfigDelegate(properties, key, default) + +@JvmName("safeNumber") +fun DisplayObject.number(default: Number, key: String? = null) = + SafeNumberConfigDelegate(properties, key, default) + +@JvmName("safeDouble") +fun DisplayObject.double(default: Double, key: String? = null) = + SafeNumberConfigDelegate(properties, key, default).double + +inline fun > DisplayObject.enum(default: E, key: String? = null) = + SafeEnumvConfigDelegate(properties, key, default) { enumValueOf(it) } \ No newline at end of file diff --git a/dataforge-vis/src/commonMain/kotlin/hep/dataforge/vis/NamedObject.kt b/dataforge-vis/src/commonMain/kotlin/hep/dataforge/vis/NamedObject.kt new file mode 100644 index 00000000..bc15d9e6 --- /dev/null +++ b/dataforge-vis/src/commonMain/kotlin/hep/dataforge/vis/NamedObject.kt @@ -0,0 +1,40 @@ +package hep.dataforge.vis + +import hep.dataforge.names.Name +import hep.dataforge.names.NameToken + +interface NamedObject : DisplayObject { + val name: String + + operator fun get(nameToken: NameToken): DisplayGroup? + + operator fun set(nameToken: NameToken, group: DisplayGroup) +} + +/** + * Recursively get a child + */ +tailrec operator fun NamedObject.get(name: Name): DisplayObject? = when (name.length) { + 0 -> this + 1 -> this[name[0]] + else -> name.first()?.let { this[it] as? NamedObject }?.get(name.cutFirst()) +} + + +/** + * Set given object creating intermediate empty groups if needed + * @param name - the full name of a child + * @param objFactory - a function that creates child object from parent (to avoid mutable parent parameter) + */ +fun NamedObject.set(name: Name, objFactory: (parent: DisplayObject) -> DisplayGroup): Unit = when (name.length) { + 0 -> error("Can't set object with empty name") + 1 -> set(name[0], objFactory(this)) + else -> (this[name.first()!!] ?: DisplayNode(this)) + .run { + if (this is NamedObject) { + this.set(name.cutFirst(), objFactory) + } else { + error("Can't assign child to a leaf element $this") + } + } +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 00000000..06efe6a4 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,7 @@ +# Enable official Kotlin Code Style in the IDE. +kotlin.code.style=official + +artifactoryUser=darksnake +artifactoryPassword=nortlander +bintrayUser=altavir +bintrayApiKey=9dcb7a779986e1b08898980269b6d428cadda0c3 \ No newline at end of file diff --git a/gradle/artifactory.gradle b/gradle/artifactory.gradle new file mode 100644 index 00000000..12e59642 --- /dev/null +++ b/gradle/artifactory.gradle @@ -0,0 +1,31 @@ +apply plugin: "com.jfrog.artifactory" + +artifactory { + def artifactory_user = project.hasProperty('artifactoryUser') ? project.property('artifactoryUser') : "" + def artifactory_password = project.hasProperty('artifactoryPassword') ? project.property('artifactoryPassword') : "" + def artifactory_contextUrl = 'http://npm.mipt.ru:8081/artifactory' + + contextUrl = artifactory_contextUrl //The base Artifactory URL if not overridden by the publisher/resolver + publish { + repository { + repoKey = 'gradle-dev-local' + username = artifactory_user + password = artifactory_password + } + + defaults { + publications('jvm', 'js', 'kotlinMultiplatform', 'metadata') + publishBuildInfo = false + publishArtifacts = true + publishPom = true + publishIvy = false + } + } + resolve { + repository { + repoKey = 'gradle-dev' + username = artifactory_user + password = artifactory_password + } + } +} \ No newline at end of file diff --git a/gradle/bintray.gradle b/gradle/bintray.gradle new file mode 100644 index 00000000..8da83c86 --- /dev/null +++ b/gradle/bintray.gradle @@ -0,0 +1,85 @@ +apply plugin: 'com.jfrog.bintray' + +def vcs = "https://github.com/mipt-npm/kmath" + +def pomConfig = { + licenses { + license { + name "The Apache Software License, Version 2.0" + url "http://www.apache.org/licenses/LICENSE-2.0.txt" + distribution "repo" + } + } + developers { + developer { + id "MIPT-NPM" + name "MIPT nuclear physics methods laboratory" + organization "MIPT" + organizationUrl "http://npm.mipt.ru" + } + } + scm { + url vcs + } +} + +project.ext.configureMavenCentralMetadata = { pom -> + def root = asNode() + root.appendNode('name', project.name) + root.appendNode('description', project.description) + root.appendNode('url', vcs) + root.children().last() + pomConfig +} + +project.ext.configurePom = pomConfig + + +// Configure publishing +publishing { + repositories { + maven { + url = "https://bintray.com/mipt-npm/scientifik" + } + } + + // Process each publication we have in this project + publications.all { publication -> + // apply changes to pom.xml files, see pom.gradle + pom.withXml(configureMavenCentralMetadata) + + + } +} + +bintray { + user = project.hasProperty('bintrayUser') ? project.property('bintrayUser') : System.getenv('BINTRAY_USER') + key = project.hasProperty('bintrayApiKey') ? project.property('bintrayApiKey') : System.getenv('BINTRAY_API_KEY') + publish = true + override = true // for multi-platform Kotlin/Native publishing + + pkg { + userOrg = "mipt-npm" + repo = "scientifik" + name = "scientifik.kmath" + issueTrackerUrl = "https://github.com/mipt-npm/kmath/issues" + licenses = ['Apache-2.0'] + vcsUrl = vcs + version { + name = project.version + vcsTag = project.version + released = new Date() + } + } +} + +bintrayUpload.dependsOn publishToMavenLocal + +// This is for easier debugging of bintray uploading problems +bintrayUpload.doFirst { + publications = project.publishing.publications.findAll { + !it.name.contains('-test') && it.name != 'kotlinMultiplatform' + }.collect { + println("Uploading artifact '$it.groupId:$it.artifactId:$it.version' from publication '$it.name'") + it.name//https://github.com/bintray/gradle-bintray-plugin/issues/256 + } +} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..87b738cb Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..558870da --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100644 index 00000000..af6708ff --- /dev/null +++ b/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 00000000..6d57edc7 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle.kts b/settings.gradle.kts index 75af1911..662388fe 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,11 +1,22 @@ pluginManagement { repositories { + mavenCentral() jcenter() - maven("https://plugins.gradle.org/m2/") + gradlePluginPortal() + maven("https://dl.bintray.com/kotlin/kotlin-eap/") + } + resolutionStrategy { + eachPlugin { + when (requested.id.id) { + "kotlinx-atomicfu" -> useModule("org.jetbrains.kotlinx:atomicfu-gradle-plugin:${requested.version}") + "kotlin-multiplatform" -> useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:${requested.version}") + "org.jetbrains.kotlin.frontend" -> useModule("org.jetbrains.kotlin:kotlin-frontend-plugin:0.0.45") + } + } } } -//enableFeaturePreview("GRADLE_METADATA") +enableFeaturePreview("GRADLE_METADATA") rootProject.name = "dataforge-core" include( @@ -15,5 +26,8 @@ include( ":dataforge-data", ":dataforge-io", ":dataforge-workspace", - ":dataforge-scripting" + ":dataforge-scripting", + ":dataforge-vis", + ":dataforge-vis:dataforge-vis-spatial", + ":dataforge-vis:dataforge-vis-fx" )