From 953bafb02e3650be9fa647423975597a20ad7fe8 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 2 Apr 2019 20:24:31 +0300 Subject: [PATCH] GDML model in progress --- build.gradle.kts | 2 +- dataforge-vis-spatial-js/build.gradle.kts | 10 +- .../hep/dataforge/vis/spatial/ThreeDemoApp.kt | 13 ++- .../hep/dataforge/vis/spatial/gdml/GDML.kt | 40 +++++++ .../dataforge/vis/spatial/gdml/GDMLNodes.kt | 106 ++++++++++++++++++ .../dataforge/vis/spatial/gdml/GDMLPlugin.kt | 35 ++++++ .../vis/spatial/gdml/ThreeGDMLFactory.kt | 73 ++++++++++++ .../vis/spatial/jsroot/JSRootDemoApp.kt | 56 +++++++-- .../dataforge/vis/spatial/jsroot/JSRootGEO.kt | 6 + .../vis/spatial/jsroot/JSRootGeometry.kt | 4 +- .../vis/spatial/jsroot/JSRootObject.kt | 15 +-- .../vis/spatial/jsroot/JSRootPlugin.kt | 13 ++- .../src/main/resources/JSRootGeoBase.js | 2 +- .../src/main/web/index.html | 10 +- settings.gradle.kts | 4 +- 15 files changed, 358 insertions(+), 31 deletions(-) create mode 100644 dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/gdml/GDML.kt create mode 100644 dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/gdml/GDMLNodes.kt create mode 100644 dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/gdml/GDMLPlugin.kt create mode 100644 dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/gdml/ThreeGDMLFactory.kt diff --git a/build.gradle.kts b/build.gradle.kts index fe4f1ca0..45e5f2da 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -13,7 +13,7 @@ buildscript { val dokkaVersion: String by rootProject.extra("0.9.17") val serializationVersion: String by rootProject.extra("0.10.0") - val dataforgeVersion: String by rootProject.extra("0.1.2-dev-2") + val dataforgeVersion: String by rootProject.extra("0.1.2-dev-3") repositories { jcenter() diff --git a/dataforge-vis-spatial-js/build.gradle.kts b/dataforge-vis-spatial-js/build.gradle.kts index 6d91fa39..a8c9bd64 100644 --- a/dataforge-vis-spatial-js/build.gradle.kts +++ b/dataforge-vis-spatial-js/build.gradle.kts @@ -8,11 +8,16 @@ plugins { id("org.jetbrains.kotlin.frontend") } +repositories{ + maven ("https://dl.bintray.com/orangy/maven" ) +} + val kotlinVersion: String by rootProject.extra dependencies { - api(project(":dataforge-vis-spatial")) + implementation(project(":dataforge-vis-spatial")) implementation("info.laht.threekt:threejs-wrapper:0.88-npm-2") + //implementation("org.jetbrains.kotlinx:kotlinx-files-js:0.1.0-dev-27") testCompile("org.jetbrains.kotlin:kotlin-test-js:$kotlinVersion") } @@ -22,6 +27,9 @@ configure { configure { dependency("three-full") dependency("style-loader") +// dependency("fs-remote") +// dependency("path") +// dependency("text-encoding") devDependency("karma") } diff --git a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/ThreeDemoApp.kt b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/ThreeDemoApp.kt index 4f68eaa6..baeb7469 100644 --- a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/ThreeDemoApp.kt +++ b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/ThreeDemoApp.kt @@ -6,7 +6,8 @@ import hep.dataforge.vis.ApplicationBase import hep.dataforge.vis.DisplayGroup import hep.dataforge.vis.require import hep.dataforge.vis.spatial.jsroot.JSRootPlugin -import hep.dataforge.vis.spatial.jsroot.jsRoot +import hep.dataforge.vis.spatial.jsroot.jsRootGeometry + import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.delay import kotlinx.coroutines.isActive @@ -60,12 +61,12 @@ class ThreeDemoApp : ApplicationBase() { } } convex { - point(50,50,50) - point(-50,-50,50) - point(-50,50,-50) - point(50,-50,-50) + point(50, 50, 50) + point(-50, -50, 50) + point(-50, 50, -50) + point(50, -50, -50) } - jsRoot { + jsRootGeometry { y = 110.0 shape = box(50, 50, 50) color(12285) diff --git a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/gdml/GDML.kt b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/gdml/GDML.kt new file mode 100644 index 00000000..c492ff96 --- /dev/null +++ b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/gdml/GDML.kt @@ -0,0 +1,40 @@ +package hep.dataforge.vis.spatial.gdml + +import hep.dataforge.meta.Config + +class GDML { + private var defines: List = emptyList() + private var solids: Map = emptyMap() + + fun define(block: GDMLDefineBuilder.() -> Unit) { + defines = GDMLDefineBuilder().apply(block).defines + } + + fun solids(block: GDMLSolidBuilder.() -> Unit) { + solids = GDMLSolidBuilder().apply(block).solids + } +} + +class GDMLDefineBuilder { + internal val defines = ArrayList() + + fun position(block: GDMLPosition.() -> Unit) { + defines.add(GDMLPosition(Config()).apply(block)) + } + + fun rotation(block: GDMLRotation.() -> Unit) { + defines.add(GDMLRotation(Config()).apply(block)) + } +} + +class GDMLSolidBuilder { + internal val solids = HashMap() + + private fun put(solid: GDMLSolid) { + solids[solid.pName!!] = solid + } + + fun box(block: GDMLBox.() -> Unit) = put(GDMLBox.build(block)) + fun tube(block: GDMLTube.() -> Unit) = put(GDMLTube.build(block)) + fun xtru(block: GDMLXtru.() -> Unit) = put(GDMLXtru.build(block)) +} \ No newline at end of file diff --git a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/gdml/GDMLNodes.kt b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/gdml/GDMLNodes.kt new file mode 100644 index 00000000..2c72bb76 --- /dev/null +++ b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/gdml/GDMLNodes.kt @@ -0,0 +1,106 @@ +package hep.dataforge.vis.spatial.gdml + +import hep.dataforge.meta.* + +sealed class GDMLNode(override val config: Config) : Specification { + var pName by string() +} + +sealed class GDMLDefine(config: Config) : GDMLNode(config) + +class GDMLPosition(config: Config) : GDMLDefine(config) { + var x by number(0f).float + var y by number(0f).float + var z by number(0f).float + var unit by string("cm") +} + +class GDMLRotation(config: Config) : GDMLDefine(config) { + var x by number(0f).float + var y by number(0f).float + var z by number(0f).float + var unit by string("deg") +} + + + +sealed class GDMLSolid(config: Config) : GDMLNode(config) { + abstract val type: String +} + +class GDMLBox(config: Config) : GDMLSolid(config) { + override val type: String = "box" + + var pDx by number().double + var pDy by number().double + var pDz by number().double + + companion object : SpecificationCompanion { + override fun wrap(config: Config): GDMLBox = GDMLBox(config) + } +} + +class GDMLTube(config: Config) : GDMLSolid(config) { + override val type: String = "tube" + + var pRMin by number().double + var pRMax by number().double + var pDz by number().double + var pSPhi by number().double + var pDPhi by number().double + + companion object : SpecificationCompanion { + override fun wrap(config: Config): GDMLTube = GDMLTube(config) + } +} + +class GDMLXtru(config: Config) : GDMLSolid(config) { + override val type: String = "xtru" + + class TwoDimVertex(val x: Double, val y: Double) + + class Section(override val config: Config) : Specification { + var zOrder by number().int + var zPosition by number().double + var xOffsset by number(0.0).double + var yOffset by number(0.0).double + var scalingFactor by number(1.0).double + + companion object : SpecificationCompanion
{ + override fun wrap(config: Config): Section = Section(config) + } + } + + val verteces + get() = config.getAll("twoDimVertex").values.map { + val x = it.node["x"].double!! + val y = it.node["y"].double!! + TwoDimVertex(x, y) + } + + val sections get() = config.getAll("section").values.map { Section(it.node!!) } + + fun vertex(x: Double, y: Double) { + config.append("twoDimVertex", TwoDimVertex(x, y)) + } + + fun section(index: Int, z: Double, block: Section.() -> Unit) { + config["section[$index]"] = Section.build(block).apply { zOrder = index; zPosition = z } + } + + companion object : SpecificationCompanion { + override fun wrap(config: Config): GDMLXtru = GDMLXtru(config) + } +} + +class GDMLUnion(config: Config) : GDMLSolid(config) { + override val type: String = "union" + + val first by node() + val second by node() + + companion object : SpecificationCompanion { + override fun wrap(config: Config): GDMLUnion = GDMLUnion(config) + } +} + diff --git a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/gdml/GDMLPlugin.kt b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/gdml/GDMLPlugin.kt new file mode 100644 index 00000000..2bb86972 --- /dev/null +++ b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/gdml/GDMLPlugin.kt @@ -0,0 +1,35 @@ +package hep.dataforge.vis.spatial.gdml + +import hep.dataforge.context.AbstractPlugin +import hep.dataforge.context.Context +import hep.dataforge.context.PluginFactory +import hep.dataforge.context.PluginTag +import hep.dataforge.vis.spatial.ThreePlugin + +class GDMLPlugin : AbstractPlugin() { + override val tag: PluginTag get() = GDMLPlugin.tag + + override fun dependsOn() = listOf(ThreePlugin) + + override fun attach(context: Context) { + super.attach(context) +// context.plugins.get()?.factories?.apply { +// this["jsRoot.geometry".toName()] = ThreeJSRootGeometryFactory +// this["jsRoot.object".toName()] = ThreeJSRootObjectFactory +// } + } + + override fun detach() { +// context.plugins.get()?.factories?.apply { +// remove("jsRoot.geometry".toName()) +// remove("jsRoot.object".toName()) +// } + super.detach() + } + + companion object : PluginFactory { + override val tag = PluginTag("vis.gdml", "hep.dataforge") + override val type = GDMLPlugin::class + override fun invoke() = GDMLPlugin() + } +} \ No newline at end of file diff --git a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/gdml/ThreeGDMLFactory.kt b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/gdml/ThreeGDMLFactory.kt new file mode 100644 index 00000000..8f3b7daf --- /dev/null +++ b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/gdml/ThreeGDMLFactory.kt @@ -0,0 +1,73 @@ +package hep.dataforge.vis.spatial.gdml + +import hep.dataforge.meta.Meta +import hep.dataforge.meta.buildMeta +import hep.dataforge.meta.toDynamic +import hep.dataforge.meta.values +import hep.dataforge.vis.DisplayLeaf +import hep.dataforge.vis.DisplayObject +import hep.dataforge.vis.int +import hep.dataforge.vis.spatial.MeshThreeFactory +import hep.dataforge.vis.spatial.jsroot.createGeometry +import hep.dataforge.vis.spatial.jsroot.createTubeBuffer +import info.laht.threekt.core.BufferGeometry + + +class GDMLShape(parent: DisplayObject?, meta: Meta, val shape: GDMLSolid) : + DisplayLeaf(parent, "$TYPE.${shape.type}", meta) { + + var facesLimit by int(0) + + companion object { + const val TYPE = "geometry.3d.gdml" + } +} + + +object ThreeGDMLFactory : MeshThreeFactory(GDMLShape::class) { + //TODO fix ineffective conversion + private fun Meta?.toJsRoot() = this?.let { + buildMeta { + values().forEach { (name, value) -> + name.toString().replaceFirst("p", "f") to value + } + } + } + + override fun buildGeometry(obj: GDMLShape): BufferGeometry { + return when (obj.shape) { + is GDMLBox -> createTubeBuffer( + obj.shape.config.toJsRoot(), + obj.facesLimit + ) + is GDMLTube -> createTubeBuffer( + obj.shape.config.toJsRoot(), + obj.facesLimit + ) + is GDMLXtru -> { + val meta = buildMeta { + val vertices = obj.shape.verteces + val zs = obj.shape.sections.sortedBy { it.zOrder!! } + "fNz" to zs.size + "fNvert" to vertices.size + "_typename" to "TGeoXtru" + "fX" to vertices.map { it.x } + "fY" to vertices.map { it.y } + "fX0" to zs.map { it.xOffsset } + "fY0" to zs.map { it.yOffset } + "fZ" to zs.map { it.zPosition!! } + "fScale" to zs.map { it.scalingFactor } + } + createGeometry(meta.toDynamic(), obj.facesLimit) + } +// is GDMLUnion -> { +// val meta = buildMeta { +// "fNode.fLeft" to obj.shape.first.toJsRoot() +// "fNode.fRight" to obj.shape.second.toJsRoot() +// "fNode._typename" to "TGeoUnion" +// } +// createGeometry(meta.toDynamic(), obj.facesLimit) +// } + } + } +} \ No newline at end of file diff --git a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/jsroot/JSRootDemoApp.kt b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/jsroot/JSRootDemoApp.kt index 697f2def..95990400 100644 --- a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/jsroot/JSRootDemoApp.kt +++ b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/jsroot/JSRootDemoApp.kt @@ -1,12 +1,18 @@ package hep.dataforge.vis.spatial.jsroot import hep.dataforge.context.Global +import hep.dataforge.meta.EmptyMeta import hep.dataforge.vis.ApplicationBase -import hep.dataforge.vis.DisplayGroup import hep.dataforge.vis.require import hep.dataforge.vis.spatial.ThreeOutput import hep.dataforge.vis.spatial.render +import org.w3c.dom.HTMLDivElement +import org.w3c.dom.events.Event +import org.w3c.files.FileList +import org.w3c.files.FileReader +import org.w3c.files.get import kotlin.browser.document +import kotlin.dom.clear class JSRootDemoApp : ApplicationBase() { @@ -23,16 +29,52 @@ class JSRootDemoApp : ApplicationBase() { Global.plugins.load(JSRootPlugin) - val renderer = ThreeOutput(Global) - renderer.start(document.getElementById("canvas")!!) - println("started") - lateinit var group: DisplayGroup - renderer.render { - jsRoot ("./geofile_full.json") + (document.getElementById("drop_zone") as? HTMLDivElement)?.apply { + addEventListener("dragover", { handleDragOver(it) }, false) + addEventListener("drop", { loadData(it) }, false) } + + } + + /** + * Handle mouse drag according to https://www.html5rocks.com/en/tutorials/file/dndfiles/ + */ + private fun handleDragOver(event: Event) { + event.stopPropagation() + event.preventDefault() + event.asDynamic().dataTransfer.dropEffect = "copy" + } + + /** + * Load data from text file + */ + private fun loadData(event: Event) { + event.stopPropagation() + event.preventDefault() + + + val file = (event.asDynamic().dataTransfer.files as FileList)[0] + ?: throw RuntimeException("Failed to load file"); + FileReader().apply { + onload = { + val string = result as String + val renderer = ThreeOutput(Global) + val canvas = document.getElementById("canvas")!! + canvas.clear() + renderer.start(canvas) + println("started") + + renderer.render { + val json = JSON.parse(string).asDynamic() + val obj = json.fVolumes.arr[0] + JSRootObject(this, EmptyMeta, obj).also { addChild(it) } + } + } + readAsText(file) + } } override fun dispose() = emptyMap()//mapOf("lines" to presenter.dispose()) diff --git a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/jsroot/JSRootGEO.kt b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/jsroot/JSRootGEO.kt index 9f587494..60a4027b 100644 --- a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/jsroot/JSRootGEO.kt +++ b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/jsroot/JSRootGEO.kt @@ -8,4 +8,10 @@ import info.laht.threekt.core.Object3D external fun createGeometry(shape: dynamic, limit: Int): BufferGeometry +external fun createCubeBuffer(shape: dynamic, limit: Int): BufferGeometry + +external fun createTubeBuffer(shape: dynamic, limit: Int): BufferGeometry + +external fun createXtruBuffer(shape: dynamic, limit: Int): BufferGeometry + external fun build(obj: dynamic, opt: dynamic): Object3D diff --git a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/jsroot/JSRootGeometry.kt b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/jsroot/JSRootGeometry.kt index bf3687e8..7351b23c 100644 --- a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/jsroot/JSRootGeometry.kt +++ b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/jsroot/JSRootGeometry.kt @@ -53,7 +53,7 @@ class JSRootGeometry(parent: DisplayObject?, meta: Meta) : DisplayLeaf(parent, T } } -fun DisplayGroup.jsRoot(meta: Meta = EmptyMeta, action: JSRootGeometry.() -> Unit = {}) = +fun DisplayGroup.jsRootGeometry(meta: Meta = EmptyMeta, action: JSRootGeometry.() -> Unit = {}) = JSRootGeometry(this, meta).apply(action).also { addChild(it) } //fun Meta.toDynamic(): dynamic { @@ -74,7 +74,7 @@ fun DisplayGroup.jsRoot(meta: Meta = EmptyMeta, action: JSRootGeometry.() -> Uni //} -object ThreeJSRootFactory : MeshThreeFactory(JSRootGeometry::class) { +object ThreeJSRootGeometryFactory : MeshThreeFactory(JSRootGeometry::class) { override fun buildGeometry(obj: JSRootGeometry): BufferGeometry { val shapeMeta = obj.shape?.toDynamic() ?: error("The shape not defined") return createGeometry(shapeMeta, obj.facesLimit) diff --git a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/jsroot/JSRootObject.kt b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/jsroot/JSRootObject.kt index cf2de9f8..f049b959 100644 --- a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/jsroot/JSRootObject.kt +++ b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/jsroot/JSRootObject.kt @@ -1,6 +1,5 @@ package hep.dataforge.vis.spatial.jsroot -import hep.dataforge.meta.DynamicMeta import hep.dataforge.meta.EmptyMeta import hep.dataforge.meta.Meta import hep.dataforge.meta.toDynamic @@ -11,9 +10,8 @@ import hep.dataforge.vis.node import hep.dataforge.vis.spatial.ThreeFactory import info.laht.threekt.core.Object3D -class JSRootObject(parent: DisplayObject?, meta: Meta) : DisplayLeaf(parent, TYPE, meta) { +class JSRootObject(parent: DisplayObject?, meta: Meta, val data: dynamic) : DisplayLeaf(parent, TYPE, meta) { - var data by node() var options by node() companion object { @@ -21,17 +19,16 @@ class JSRootObject(parent: DisplayObject?, meta: Meta) : DisplayLeaf(parent, TYP } } -object JSRootObjectFactory : ThreeFactory { +object ThreeJSRootObjectFactory : ThreeFactory { override val type = JSRootObject::class override fun invoke(obj: JSRootObject): Object3D { - return build(obj.data?.toDynamic(), obj.options?.toDynamic()) + return build(obj.data, obj.options?.toDynamic()) } } -fun DisplayGroup.jsRoot(path: String) { - JSRootObject(this, EmptyMeta).apply{ - data = DynamicMeta(hep.dataforge.vis.require(path)) - }.also { addChild(it) } +fun DisplayGroup.jsRootObject(str: String) { + val json = JSON.parse(str) + JSRootObject(this, EmptyMeta, json).also { addChild(it) } } \ No newline at end of file diff --git a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/jsroot/JSRootPlugin.kt b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/jsroot/JSRootPlugin.kt index 29676b04..7a31f2bd 100644 --- a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/jsroot/JSRootPlugin.kt +++ b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/jsroot/JSRootPlugin.kt @@ -14,7 +14,18 @@ class JSRootPlugin : AbstractPlugin() { override fun attach(context: Context) { super.attach(context) - context.plugins.get()?.factories?.set("ThreeJSRootFactory".toName(), ThreeJSRootFactory) + context.plugins.get()?.factories?.apply { + this["jsRoot.geometry".toName()] = ThreeJSRootGeometryFactory + this["jsRoot.object".toName()] = ThreeJSRootObjectFactory + } + } + + override fun detach() { + context.plugins.get()?.factories?.apply { + remove("jsRoot.geometry".toName()) + remove("jsRoot.object".toName()) + } + super.detach() } companion object: PluginFactory { diff --git a/dataforge-vis-spatial-js/src/main/resources/JSRootGeoBase.js b/dataforge-vis-spatial-js/src/main/resources/JSRootGeoBase.js index 82df4b08..21f442c1 100644 --- a/dataforge-vis-spatial-js/src/main/resources/JSRootGeoBase.js +++ b/dataforge-vis-spatial-js/src/main/resources/JSRootGeoBase.js @@ -3428,7 +3428,7 @@ obj = null; } - if (opt.composite && shape && (shape._typename == 'TGeoCompositeShape') && shape.fNode) + if (opt.composite && shape && (shape._typename === 'TGeoCompositeShape') && shape.fNode) obj = GEO.buildCompositeVolume(shape); if (!obj && shape) diff --git a/dataforge-vis-spatial-js/src/main/web/index.html b/dataforge-vis-spatial-js/src/main/web/index.html index fc44c722..cc233d60 100644 --- a/dataforge-vis-spatial-js/src/main/web/index.html +++ b/dataforge-vis-spatial-js/src/main/web/index.html @@ -4,7 +4,7 @@ Three js demo for particle physics @@ -14,9 +14,15 @@ - + +
+ Загрузить данные +
+ (перетащить файл сюда) +

Demo canvas

diff --git a/settings.gradle.kts b/settings.gradle.kts index d1e3c70c..45e4afa4 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -24,4 +24,6 @@ include( ":dataforge-vis-spatial", ":dataforge-vis-spatial-fx", ":dataforge-vis-spatial-js" -) \ No newline at end of file +) + +includeBuild("../dataforge-core") \ No newline at end of file