diff --git a/build.gradle.kts b/build.gradle.kts index 851ee471..fc4d14f0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,4 +1,4 @@ -val dataforgeVersion by extra("0.1.3-dev-7") +val dataforgeVersion by extra("0.1.3-dev-9") plugins{ kotlin("jvm") version "1.3.40" apply false diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/DisplayGroup.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/DisplayGroup.kt index 8ceb40d5..e7330f52 100644 --- a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/DisplayGroup.kt +++ b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/DisplayGroup.kt @@ -17,19 +17,15 @@ class DisplayGroup( private val namedChildren = HashMap() private val unnamedChildren = ArrayList() - override val defaultTarget: String get() = DisplayObject.TARGET + override val defaultTarget: String get() = DisplayObject.TYPE override val properties: Styled = Styled(meta) override fun iterator(): Iterator = (namedChildren.values + unnamedChildren).iterator() - override fun listNames(target: String): Sequence = - namedChildren.keys.asSequence() - - override fun provideTop(target: String, name: Name): Any? { - return if (target == defaultTarget) { - namedChildren[name] - } else { - null + override fun provideTop(target: String): Map { + return when(target){ + DisplayObject.TYPE -> namedChildren + else -> emptyMap() } } @@ -55,14 +51,18 @@ class DisplayGroup( /** * */ - operator fun set(key: String, child: DisplayObject?) { - val name = key.toName() - if (child == null) { - namedChildren.remove(name) + operator fun set(key: String?, child: DisplayObject?) { + if(key == null){ + } else { - namedChildren[name] = child + val name = key.toName() + if (child == null) { + namedChildren.remove(name) + } else { + namedChildren[name] = child + } + listeners.forEach { it.callback(name, child) } } - listeners.forEach { it.callback(name, child) } } /** diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/DisplayObject.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/DisplayObject.kt index 79952c4a..5639326b 100644 --- a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/DisplayObject.kt +++ b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/DisplayObject.kt @@ -3,13 +3,16 @@ package hep.dataforge.vis.common import hep.dataforge.meta.* import hep.dataforge.names.Name import hep.dataforge.names.toName +import hep.dataforge.provider.Type import hep.dataforge.vis.common.DisplayObject.Companion.META_KEY import hep.dataforge.vis.common.DisplayObject.Companion.TAGS_KEY +import hep.dataforge.vis.common.DisplayObject.Companion.TYPE /** * A root type for display hierarchy */ -interface DisplayObject { +@Type(TYPE) +interface DisplayObject : MetaRepr { /** * The parent object of this one. If null, this one is a root. @@ -18,8 +21,12 @@ interface DisplayObject { val properties: Styled + override fun toMeta(): Meta = buildMeta(properties) { + "type" to this::class + } + companion object { - const val TARGET = "display" + const val TYPE = "display" const val DEFAULT_TYPE = "" //const val TYPE_KEY = "@type" diff --git a/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/fx/FXPlugin.kt b/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/fx/FXPlugin.kt index 97d3ce79..12e9c4a3 100644 --- a/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/fx/FXPlugin.kt +++ b/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/fx/FXPlugin.kt @@ -115,5 +115,5 @@ class ApplicationSurrogate : App() { } fun Context.display(width: Double = 800.0, height: Double = 600.0, component: () -> UIComponent) { - plugins.getOrLoad().display(component(), width, height) + plugins.fetch(FXPlugin).display(component(), width, height) } \ No newline at end of file diff --git a/dataforge-vis-spatial-js/build.gradle.kts b/dataforge-vis-spatial-js/build.gradle.kts index 0c0767f5..68be367d 100644 --- a/dataforge-vis-spatial-js/build.gradle.kts +++ b/dataforge-vis-spatial-js/build.gradle.kts @@ -13,8 +13,7 @@ val kotlinVersion: String by rootProject.extra dependencies { implementation(project(":dataforge-vis-spatial")) - //implementation("ch.viseon.threejs:wrapper:105.0.0") - implementation("info.laht.threekt:threejs-wrapper:0.88-npm-2") + implementation("info.laht.threekt:threejs-wrapper:0.106-npm-2") testCompile(kotlin("test-js")) } @@ -22,7 +21,7 @@ configure { downloadNodeJsVersion = "latest" configure { - dependency("three-full") + dependency("three","0.106.2") dependency("@hi-level/three-csg") dependency("style-loader") dependency("element-resize-event") diff --git a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/ThreeOutput.kt b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/ThreeOutput.kt deleted file mode 100644 index 25f08f89..00000000 --- a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/ThreeOutput.kt +++ /dev/null @@ -1,102 +0,0 @@ -package hep.dataforge.vis.spatial - -import hep.dataforge.context.Context -import hep.dataforge.context.content -import hep.dataforge.meta.* -import hep.dataforge.output.Output -import hep.dataforge.vis.common.DisplayGroup -import hep.dataforge.vis.common.DisplayObject -import hep.dataforge.vis.spatial.demo.require -import hep.dataforge.vis.spatial.three.Group -import info.laht.threekt.WebGLRenderer -import info.laht.threekt.cameras.PerspectiveCamera -import info.laht.threekt.core.Object3D -import info.laht.threekt.external.controls.OrbitControls -import info.laht.threekt.helpers.AxesHelper -import info.laht.threekt.lights.AmbientLight -import info.laht.threekt.math.ColorConstants -import info.laht.threekt.scenes.Scene -import org.w3c.dom.Element -import kotlin.browser.window - -private val elementResizeEvent = require("element-resize-event") - -class ThreeOutput(override val context: Context, val meta: Meta = EmptyMeta) : Output { - - private val aspectRatio by meta.number(1.0).double - - val scene: Scene = Scene().apply { - add(AmbientLight()) - if (meta["axis"] != null) { - val axesHelper = AxesHelper(meta["axis.size"].int ?: 1) - add(axesHelper) - } - } - - val camera = PerspectiveCamera( - meta["camera.fov"].int ?: 75, - aspectRatio, - meta["camera.nearClip"].double ?: World.CAMERA_NEAR_CLIP, - meta["camera.farClip"].double ?: World.CAMERA_FAR_CLIP - ).apply { - position.setZ(World.CAMERA_INITIAL_DISTANCE) - rotation.set(World.CAMERA_INITIAL_X_ANGLE, World.CAMERA_INITIAL_Y_ANGLE, World.CAMERA_INITIAL_Z_ANGLE) - } - - fun attach(element: Element, computeWidth: Element.() -> Int = { element.clientWidth }) { - val width by meta.number(computeWidth(element)).int - - val height: Int = (width / aspectRatio).toInt() - - val renderer = WebGLRenderer { antialias = true }.apply { - setClearColor(ColorConstants.skyblue, 1) - setSize(width, height) - } - - val controls: OrbitControls = OrbitControls(camera, renderer.domElement) - - fun animate() { - window.requestAnimationFrame { - animate() - } - renderer.render(scene, camera) - } - - elementResizeEvent(element) { - camera.updateProjectionMatrix() - val newWidth = computeWidth(element) - renderer.setSize(newWidth, (newWidth / aspectRatio).toInt()) - } - - element.replaceWith(renderer.domElement) - animate() - } - - private fun buildNode(obj: DisplayObject): Object3D? { - return if (obj is DisplayGroup) { - Group(obj.mapNotNull { buildNode(it) }).apply { - updatePosition(obj) - } - } else { - //find specialized factory for this type if it is present - val factory = context.content>(ThreeFactory.TYPE).values.find { it.type == obj::class } - when { - factory != null -> factory(obj) - obj is Shape -> ThreeShapeFactory(obj) - else -> error("Renderer for ${obj::class} not found") - } - } - } - - override fun render(obj: DisplayObject, meta: Meta) { - buildNode(obj)?.let { - scene.add(it) - } ?: error("Renderer for ${obj::class} not found") - } - - - companion object { - fun build(context: Context, meta: Meta = EmptyMeta, override: MetaBuilder.() -> Unit) = - ThreeOutput(context, buildMeta(meta, override)) - } -} \ No newline at end of file diff --git a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/ThreePlugin.kt b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/ThreePlugin.kt deleted file mode 100644 index 11aeba52..00000000 --- a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/ThreePlugin.kt +++ /dev/null @@ -1,39 +0,0 @@ -package hep.dataforge.vis.spatial - -import hep.dataforge.context.AbstractPlugin -import hep.dataforge.context.PluginFactory -import hep.dataforge.context.PluginTag -import hep.dataforge.meta.Meta -import hep.dataforge.names.Name -import hep.dataforge.names.set - -class ThreePlugin : AbstractPlugin() { - override val tag: PluginTag get() = ThreePlugin.tag - - val factories = HashMap>() - - init { - factories["box"] = ThreeBoxFactory - factories["convex"] = ThreeConvexFactory - } - - override fun listNames(target: String): Sequence { - return when (target) { - ThreeFactory.TYPE -> factories.keys.asSequence() - else -> return super.listNames(target) - } - } - - override fun provideTop(target: String, name: Name): Any? { - return when (target) { - ThreeFactory.TYPE -> factories[name] - else -> return super.provideTop(target, name) - } - } - - companion object : PluginFactory { - override val tag = PluginTag("vis.three", "hep.dataforge") - override val type = ThreePlugin::class - override fun invoke(meta: Meta) = ThreePlugin() - } -} \ No newline at end of file diff --git a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/demo/ThreeDemoApp.kt b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/demo/ThreeDemoApp.kt index b2e1e1f1..0a63abb7 100644 --- a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/demo/ThreeDemoApp.kt +++ b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/demo/ThreeDemoApp.kt @@ -2,6 +2,7 @@ package hep.dataforge.vis.spatial.demo import hep.dataforge.context.ContextBuilder import hep.dataforge.meta.number +import hep.dataforge.vis.common.Colors import hep.dataforge.vis.spatial.* import hep.dataforge.vis.spatial.jsroot.JSRootPlugin import hep.dataforge.vis.spatial.jsroot.jsRootGeometry @@ -28,7 +29,7 @@ class ThreeDemoApp : ApplicationBase() { }.build() context.plugins.load(ThreeDemoGrid()).run { - demo("group", "Group demo") { + demo("dynamic", "Dynamic properties demo") { val group = group { box { z = 110.0 @@ -42,6 +43,7 @@ class ThreeDemoApp : ApplicationBase() { xSize = 100.0 ySize = 100.0 zSize = 100.0 + //override color for this cube color(1530) GlobalScope.launch { @@ -53,13 +55,13 @@ class ThreeDemoApp : ApplicationBase() { } } - var color by group.properties.number(1530).int + var material by group.properties.number(1530).int GlobalScope.launch { val random = Random(111) while (isActive) { delay(1000) - color = random.nextInt(0, Int.MAX_VALUE) + material = random.nextInt(0, Int.MAX_VALUE) } } } @@ -68,7 +70,9 @@ class ThreeDemoApp : ApplicationBase() { jsRootGeometry { y = 110.0 shape = box(50, 50, 50) - color(12285) + color(Colors.lightcoral) + rotationX = PI / 4 + rotationY = PI / 4 } } @@ -81,6 +85,40 @@ class ThreeDemoApp : ApplicationBase() { layer(i * 5, 20 * sin(2 * PI / 100 * i), 20 * cos(2 * PI / 100 * i)) } } + + color(Colors.teal) + } + + demo("CSG", "CSG operations") { + composite(CompositeType.UNION) { + box(100, 100, 100) { + z = 100 + rotationX = PI / 4 + rotationY = PI / 4 + } + box(100, 100, 100) + color(Colors.green) + } + composite(CompositeType.INTERSECT) { + box(100, 100, 100) { + z = 100 + rotationX = PI / 4 + rotationY = PI / 4 + } + box(100, 100, 100) + y = 300 + color(Colors.red) + } + composite(CompositeType.SUBTRACT) { + box(100, 100, 100) { + z = 100 + rotationX = PI / 4 + rotationY = PI / 4 + } + box(100, 100, 100) + y = -300 + color(Colors.blue) + } } } diff --git a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/demo/ThreeDemoGrid.kt b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/demo/ThreeDemoGrid.kt index 68b2da9a..4b179c31 100644 --- a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/demo/ThreeDemoGrid.kt +++ b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/demo/ThreeDemoGrid.kt @@ -14,8 +14,10 @@ import hep.dataforge.output.Output import hep.dataforge.output.OutputManager import hep.dataforge.vis.common.DisplayGroup import hep.dataforge.vis.common.DisplayObject -import hep.dataforge.vis.spatial.ThreeOutput import hep.dataforge.vis.spatial.render +import hep.dataforge.vis.spatial.three.ThreeOutput +import hep.dataforge.vis.spatial.three.ThreePlugin +import hep.dataforge.vis.spatial.three.output import kotlinx.html.dom.append import kotlinx.html.dom.create import kotlinx.html.h2 @@ -33,6 +35,8 @@ class ThreeDemoGrid(meta: Meta) : AbstractPlugin(meta), OutputManager { private val gridRoot = document.create.div("row") private val outputs: MutableMap = HashMap() + override fun dependsOn(): List> = listOf(ThreePlugin) + override fun attach(context: Context) { super.attach(context) val elementId = meta["elementID"].string ?: "canvas" @@ -42,9 +46,11 @@ class ThreeDemoGrid(meta: Meta) : AbstractPlugin(meta), OutputManager { } override fun get(type: KClass, name: Name, stage: Name, meta: Meta): Output { + val three = context.plugins.get()!! + return outputs.getOrPut(name) { if (type != DisplayObject::class) error("Supports only DisplayObject") - val output = ThreeOutput.build(context, meta) { + val output = three.output(meta) { "axis" to { "size" to 500 } @@ -52,8 +58,8 @@ class ThreeDemoGrid(meta: Meta) : AbstractPlugin(meta), OutputManager { //TODO calculate cell width here using jquery gridRoot.append { span("border") { - div("col-4") { - output.attach(div { id = "output-$name" }){ 300} + div("col-6") { + output.attach(div { id = "output-$name" }) { 500 } hr() h2 { +(meta["title"].string ?: name.toString()) } } 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 index 364f5637..2f47760d 100644 --- 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 @@ -5,8 +5,10 @@ import hep.dataforge.context.Context import hep.dataforge.context.PluginFactory import hep.dataforge.context.PluginTag import hep.dataforge.meta.Meta +import hep.dataforge.names.Name import hep.dataforge.names.toName -import hep.dataforge.vis.spatial.ThreePlugin +import hep.dataforge.vis.spatial.three.ThreeFactory +import hep.dataforge.vis.spatial.three.ThreePlugin import kotlin.reflect.KClass class GDMLPlugin : AbstractPlugin() { @@ -14,21 +16,13 @@ class GDMLPlugin : AbstractPlugin() { override fun dependsOn() = listOf(ThreePlugin) - override fun attach(context: Context) { - super.attach(context) - context.plugins.get()?.factories?.apply { - this["gdml".toName()] = ThreeGDMLFactory + override fun provideTop(target: String): Map { + return when(target){ + ThreeFactory.TYPE-> mapOf("gdml".toName() to ThreeGDMLFactory) + else -> emptyMap() } } - 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: KClass = GDMLPlugin::class 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 index 5288cb02..0d335c37 100644 --- 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 @@ -7,7 +7,7 @@ import hep.dataforge.meta.values import hep.dataforge.vis.common.DisplayLeaf import hep.dataforge.vis.common.DisplayObject import hep.dataforge.vis.common.int -import hep.dataforge.vis.spatial.MeshThreeFactory +import hep.dataforge.vis.spatial.three.MeshThreeFactory import hep.dataforge.vis.spatial.jsroot.createCubeBuffer import hep.dataforge.vis.spatial.jsroot.createGeometry import hep.dataforge.vis.spatial.jsroot.createTubeBuffer 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 b4b64467..cf7d1629 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 @@ -2,9 +2,11 @@ package hep.dataforge.vis.spatial.jsroot import hep.dataforge.context.Global import hep.dataforge.meta.EmptyMeta -import hep.dataforge.vis.spatial.ThreeOutput +import hep.dataforge.vis.spatial.three.ThreeOutput import hep.dataforge.vis.spatial.demo.ApplicationBase import hep.dataforge.vis.spatial.render +import hep.dataforge.vis.spatial.three.ThreePlugin +import hep.dataforge.vis.spatial.three.output import org.w3c.dom.HTMLDivElement import org.w3c.dom.events.Event import org.w3c.files.FileList @@ -59,7 +61,7 @@ class JSRootDemoApp : ApplicationBase() { FileReader().apply { onload = { val string = result as String - val renderer = ThreeOutput(Global) + val renderer = Global.plugins.fetch(ThreePlugin).output() val canvas = document.getElementById("canvas")!! canvas.clear() renderer.attach(canvas) 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 54e3a0ef..7eb159ab 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 @@ -5,7 +5,7 @@ import hep.dataforge.meta.Meta import hep.dataforge.meta.buildMeta import hep.dataforge.meta.toDynamic import hep.dataforge.vis.common.* -import hep.dataforge.vis.spatial.MeshThreeFactory +import hep.dataforge.vis.spatial.three.MeshThreeFactory import info.laht.threekt.core.BufferGeometry class JSRootGeometry(parent: DisplayObject?, meta: Meta) : DisplayLeaf(parent, meta) { 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 a62f6f61..9bf23b3a 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 @@ -7,7 +7,7 @@ import hep.dataforge.vis.common.DisplayGroup import hep.dataforge.vis.common.DisplayLeaf import hep.dataforge.vis.common.DisplayObject import hep.dataforge.vis.common.node -import hep.dataforge.vis.spatial.ThreeFactory +import hep.dataforge.vis.spatial.three.ThreeFactory import info.laht.threekt.core.Object3D class JSRootObject(parent: DisplayObject?, meta: Meta, val data: dynamic) : DisplayLeaf(parent, meta) { 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 46bd0f24..f0a7cb73 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 @@ -1,34 +1,29 @@ package hep.dataforge.vis.spatial.jsroot import hep.dataforge.context.AbstractPlugin -import hep.dataforge.context.Context import hep.dataforge.context.PluginFactory import hep.dataforge.context.PluginTag import hep.dataforge.meta.Meta +import hep.dataforge.names.Name import hep.dataforge.names.toName -import hep.dataforge.vis.spatial.ThreePlugin +import hep.dataforge.vis.spatial.three.ThreeFactory +import hep.dataforge.vis.spatial.three.ThreePlugin class JSRootPlugin : AbstractPlugin() { override val tag: PluginTag get() = JSRootPlugin.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 provideTop(target: String): Map { + return when(target){ + ThreeFactory.TYPE -> mapOf( + "jsRoot.geometry".toName() to ThreeJSRootGeometryFactory, + "jsRoot.object".toName() to ThreeJSRootObjectFactory + ) + else -> emptyMap() } } - 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.jsroot", "hep.dataforge") override val type = JSRootPlugin::class diff --git a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/three/ConvexGeometry.kt b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/three/ConvexGeometry.kt deleted file mode 100644 index 40ebf221..00000000 --- a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/three/ConvexGeometry.kt +++ /dev/null @@ -1,11 +0,0 @@ -@file:JsModule("three-full") -@file:JsNonModule -package hep.dataforge.vis.spatial.three - -import info.laht.threekt.core.BufferGeometry -import info.laht.threekt.core.Geometry -import info.laht.threekt.math.Vector3 - -external class ConvexGeometry(points: Array) : Geometry - -external class ConvexBufferGeometry(points: Array) : BufferGeometry \ No newline at end of file diff --git a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/three/EdgesGeometry.kt b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/three/EdgesGeometry.kt deleted file mode 100644 index 48a493c7..00000000 --- a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/three/EdgesGeometry.kt +++ /dev/null @@ -1,11 +0,0 @@ -@file:JsModule("three-full") -@file:JsNonModule - -package hep.dataforge.vis.spatial.three - -import info.laht.threekt.core.BufferGeometry -import info.laht.threekt.core.Geometry - -external class EdgesGeometry(geometry: Geometry, thresholdAngle: Int = definedExternally) : BufferGeometry { - constructor(geometry: BufferGeometry, thresholdAngle: Int = definedExternally) -} \ No newline at end of file diff --git a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/Materials.kt b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/three/Materials.kt similarity index 97% rename from dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/Materials.kt rename to dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/three/Materials.kt index 3fe1073d..74c6021b 100644 --- a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/Materials.kt +++ b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/three/Materials.kt @@ -1,4 +1,4 @@ -package hep.dataforge.vis.spatial +package hep.dataforge.vis.spatial.three import hep.dataforge.meta.MetaItem import hep.dataforge.meta.double diff --git a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/three/ThreeCompositeFactory.kt b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/three/ThreeCompositeFactory.kt new file mode 100644 index 00000000..2969e865 --- /dev/null +++ b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/three/ThreeCompositeFactory.kt @@ -0,0 +1,31 @@ +package hep.dataforge.vis.spatial.three + +import hep.dataforge.vis.spatial.Composite +import hep.dataforge.vis.spatial.CompositeType +import info.laht.threekt.core.BufferGeometry +import info.laht.threekt.core.Geometry +import info.laht.threekt.objects.Mesh + +/** + * This should be inner, becaulse it uses object builder + */ +class ThreeCompositeFactory(val three: ThreePlugin) : MeshThreeFactory(Composite::class) { + + override fun buildGeometry(obj: Composite): BufferGeometry { + val first = three.buildObject3D(obj.first) as? Mesh ?: error("First part of composite is not a mesh") + first.updateMatrix() + val second = three.buildObject3D(obj.second) as? Mesh ?: error("Second part of composite is not a mesh") + second.updateMatrix() + val firstCSG = CSG.fromMesh(first) + val secondCSG = CSG.fromMesh(second) + val resultCSG = when (obj.type) { + CompositeType.UNION -> firstCSG.union(secondCSG) + CompositeType.INTERSECT -> firstCSG.intersect(secondCSG) + CompositeType.SUBTRACT -> firstCSG.subtract(secondCSG) + } + + val mesh = CSG.toMesh(resultCSG, second.matrix) + return (mesh.geometry as Geometry).toBufferGeometry() + } + +} \ No newline at end of file diff --git a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/ThreeFactory.kt b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/three/ThreeFactory.kt similarity index 67% rename from dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/ThreeFactory.kt rename to dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/three/ThreeFactory.kt index 339864d5..d42526db 100644 --- a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/ThreeFactory.kt +++ b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/three/ThreeFactory.kt @@ -1,4 +1,4 @@ -package hep.dataforge.vis.spatial +package hep.dataforge.vis.spatial.three import hep.dataforge.meta.boolean import hep.dataforge.names.startsWith @@ -7,14 +7,14 @@ import hep.dataforge.provider.Type import hep.dataforge.vis.common.DisplayObject import hep.dataforge.vis.common.getProperty import hep.dataforge.vis.common.onChange -import hep.dataforge.vis.spatial.ThreeFactory.Companion.TYPE -import hep.dataforge.vis.spatial.ThreeFactory.Companion.buildMesh -import hep.dataforge.vis.spatial.three.ConvexBufferGeometry -import hep.dataforge.vis.spatial.three.EdgesGeometry -import hep.dataforge.vis.spatial.three.euler +import hep.dataforge.vis.spatial.* +import hep.dataforge.vis.spatial.three.ThreeFactory.Companion.TYPE +import hep.dataforge.vis.spatial.three.ThreeFactory.Companion.buildMesh import info.laht.threekt.core.BufferGeometry import info.laht.threekt.core.Object3D +import info.laht.threekt.external.geometries.ConvexBufferGeometry import info.laht.threekt.geometries.BoxBufferGeometry +import info.laht.threekt.geometries.EdgesGeometry import info.laht.threekt.geometries.WireframeGeometry import info.laht.threekt.objects.LineSegments import info.laht.threekt.objects.Mesh @@ -35,7 +35,12 @@ interface ThreeFactory { companion object { const val TYPE = "threeFactory" - internal fun buildMesh(obj: DisplayObject, geometry: BufferGeometry): Mesh { + fun buildMesh(obj: T, geometryBuilder: (T) -> BufferGeometry): Mesh { + val geometry = geometryBuilder(obj) + + //JS sometimes tries to pass Geometry as BufferGeometry + @Suppress("USELESS_IS_CHECK") if (geometry !is BufferGeometry) error("BufferGeometry expected") + val mesh = Mesh(geometry, obj.material) //inherited edges definition, enabled by default @@ -49,6 +54,29 @@ interface ThreeFactory { val material = obj.getProperty("edges.material")?.material() ?: Materials.DEFAULT mesh.add(LineSegments(WireframeGeometry(mesh.geometry as BufferGeometry), material)) } + + //set position for meseh + mesh.updatePosition(obj) + + //add listener to object properties + obj.onChange(this) { name, _, _ -> + if (name.toString() == "material") { + //updated material + mesh.material = obj.material + } else if ( + name.startsWith("pos".toName()) || + name.startsWith("scale".toName()) || + name.startsWith("rotation".toName()) || + name.toString() == "visible" + ) { + //update position of mesh using this object + mesh.updatePosition(obj) + } else { + //full update + mesh.geometry = geometryBuilder(obj) + mesh.material = obj.material + } + } return mesh } } @@ -78,7 +106,8 @@ operator fun ThreeFactory.invoke(obj: Any): Object3D { /** * Basic geometry-based factory */ -abstract class MeshThreeFactory(override val type: KClass) : ThreeFactory { +abstract class MeshThreeFactory(override val type: KClass) : + ThreeFactory { /** * Build a geometry for an object */ @@ -86,36 +115,8 @@ abstract class MeshThreeFactory(override val type: KClass - if (name.toString() == "material") { - //updated material - mesh.material = obj.material - } else if ( - name.startsWith("pos".toName()) || - name.startsWith("scale".toName()) || - name.startsWith("rotation".toName()) || - name.toString() == "visible" - ) { - //update position of mesh using this object - mesh.updatePosition(obj) - } else { - //full update - mesh.geometry = buildGeometry(obj) - mesh.material = obj.material - } - } + val mesh = buildMesh(obj, ::buildGeometry) return mesh } } diff --git a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/ThreeGeometryBuilder.kt b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/three/ThreeGeometryBuilder.kt similarity index 71% rename from dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/ThreeGeometryBuilder.kt rename to dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/three/ThreeGeometryBuilder.kt index 699da72d..86b8bd27 100644 --- a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/ThreeGeometryBuilder.kt +++ b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/three/ThreeGeometryBuilder.kt @@ -1,13 +1,13 @@ -package hep.dataforge.vis.spatial +package hep.dataforge.vis.spatial.three import hep.dataforge.meta.Meta import hep.dataforge.meta.get import hep.dataforge.meta.int -import hep.dataforge.vis.spatial.three.toBufferGeometry +import hep.dataforge.vis.spatial.GeometryBuilder +import hep.dataforge.vis.spatial.Point3D import info.laht.threekt.core.BufferGeometry import info.laht.threekt.core.Face3 import info.laht.threekt.core.Geometry -import info.laht.threekt.math.Color import info.laht.threekt.math.Vector3 // TODO use unsafe cast instead @@ -32,24 +32,18 @@ class ThreeGeometryBuilder : GeometryBuilder { } override fun face(vertex1: Point3D, vertex2: Point3D, vertex3: Point3D, normal: Point3D?, meta: Meta) { - val materialIndex = meta["materialIndex"].int ?: 0 - val color = meta["color"]?.color() ?: Color() - faces.add( - Face3( - append(vertex1), - append(vertex2), - append(vertex3), - normal?.asVector() ?: Vector3(0, 0, 0), - color, - materialIndex - ) - ) + val face = Face3(append(vertex1), append(vertex2), append(vertex3), normal?.asVector() ?: Vector3(0, 0, 0)) + meta["materialIndex"].int?.let { face.materialIndex = it } + meta["color"]?.color()?.let { face.color = it } + faces.add(face) } override fun build(): BufferGeometry { return Geometry().apply { vertices = this@ThreeGeometryBuilder.vertices.map { it.asVector() }.toTypedArray() faces = this@ThreeGeometryBuilder.faces.toTypedArray() + computeBoundingSphere() + computeFaceNormals() }.toBufferGeometry() } } \ No newline at end of file diff --git a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/three/ThreeOutput.kt b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/three/ThreeOutput.kt new file mode 100644 index 00000000..d1f06e2c --- /dev/null +++ b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/three/ThreeOutput.kt @@ -0,0 +1,67 @@ +package hep.dataforge.vis.spatial.three + +import hep.dataforge.context.Context +import hep.dataforge.meta.* +import hep.dataforge.output.Output +import hep.dataforge.vis.common.Colors +import hep.dataforge.vis.common.DisplayObject +import hep.dataforge.vis.spatial.demo.require +import info.laht.threekt.WebGLRenderer +import info.laht.threekt.helpers.AxesHelper +import info.laht.threekt.lights.AmbientLight +import info.laht.threekt.scenes.Scene +import org.w3c.dom.Element +import kotlin.browser.window + +private val elementResizeEvent = require("element-resize-event") + +class ThreeOutput(val three: ThreePlugin, val meta: Meta = EmptyMeta) : Output { + + override val context: Context get() = three.context + + val scene: Scene = Scene().apply { + add(AmbientLight()) + if (meta["axis"] != null) { + val axesHelper = AxesHelper(meta["axis.size"].int ?: 1) + add(axesHelper) + } + } + + private val camera = three.buildCamera(meta["camera"].node ?: EmptyMeta) + + fun attach(element: Element, computeWidth: Element.() -> Int = { element.clientWidth }) { + val width by meta.number(computeWidth(element)).int + + val height: Int = (width / camera.aspect).toInt() + + val renderer = WebGLRenderer { antialias = true }.apply { + setClearColor(Colors.skyblue, 1) + setSize(width, height) + } + + three.addControls(camera,renderer.domElement, meta["controls"].node?:EmptyMeta) + + fun animate() { + window.requestAnimationFrame { + animate() + } + renderer.render(scene, camera) + } + + elementResizeEvent(element) { + camera.updateProjectionMatrix() + val newWidth = computeWidth(element) + renderer.setSize(newWidth, (newWidth / camera.aspect).toInt()) + } + + element.replaceWith(renderer.domElement) + animate() + } + + override fun render(obj: DisplayObject, meta: Meta) { + scene.add(three.buildObject3D(obj)) + } +} + +fun ThreePlugin.output(meta: Meta = EmptyMeta, override: MetaBuilder.() -> Unit = {}) = + ThreeOutput(this, buildMeta(meta, override)) \ No newline at end of file diff --git a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/three/ThreePlugin.kt b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/three/ThreePlugin.kt new file mode 100644 index 00000000..dc555446 --- /dev/null +++ b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/three/ThreePlugin.kt @@ -0,0 +1,81 @@ +package hep.dataforge.vis.spatial.three + +import hep.dataforge.context.AbstractPlugin +import hep.dataforge.context.PluginFactory +import hep.dataforge.context.PluginTag +import hep.dataforge.context.content +import hep.dataforge.meta.* +import hep.dataforge.vis.common.DisplayGroup +import hep.dataforge.vis.common.DisplayObject +import hep.dataforge.vis.spatial.* +import info.laht.threekt.cameras.Camera +import info.laht.threekt.cameras.PerspectiveCamera +import info.laht.threekt.core.Object3D +import info.laht.threekt.external.controls.OrbitControls +import info.laht.threekt.external.controls.TrackballControls +import org.w3c.dom.Node +import kotlin.collections.set +import kotlin.reflect.KClass + +class ThreePlugin : AbstractPlugin() { + override val tag: PluginTag get() = Companion.tag + + private val objectFactories = HashMap, ThreeFactory<*>>() + private val compositeFactory = ThreeCompositeFactory(this) + + init { + //Add specialized factories here + objectFactories[Box::class] = ThreeBoxFactory + objectFactories[Convex::class] = ThreeConvexFactory + } + + private fun findObjectFactory(type: KClass): ThreeFactory<*>? { + return objectFactories[type] + ?: context.content>(ThreeFactory.TYPE).values.find { it.type == type } + } + + fun buildObject3D(obj: DisplayObject): Object3D { + return when (obj) { + is DisplayGroup -> Group(obj.map { buildObject3D(it) }).apply { + updatePosition(obj) + } + is Composite -> compositeFactory(obj) + else -> { + //find specialized factory for this type if it is present + val factory = findObjectFactory(obj::class) + when { + factory != null -> factory(obj) + obj is Shape -> ThreeShapeFactory(obj) + else -> error("Renderer for ${obj::class} not found") + } + } + } + } + + fun buildCamera(meta: Meta) = PerspectiveCamera( + meta["fov"].int ?: 75, + meta["aspect"].double ?: 1.0, + meta["nearClip"].double ?: World.CAMERA_NEAR_CLIP, + meta["farClip"].double ?: World.CAMERA_FAR_CLIP + ).apply { + position.setZ(World.CAMERA_INITIAL_DISTANCE) + rotation.set( + World.CAMERA_INITIAL_X_ANGLE, + World.CAMERA_INITIAL_Y_ANGLE, + World.CAMERA_INITIAL_Z_ANGLE + ) + } + + fun addControls(camera: Camera, element: Node, meta: Meta) { + when (meta["type"].string) { + "trackball" -> TrackballControls(camera, element) + else -> OrbitControls(camera, element) + } + } + + companion object : PluginFactory { + override val tag = PluginTag("vis.three", "hep.dataforge") + override val type = ThreePlugin::class + override fun invoke(meta: Meta) = ThreePlugin() + } +} \ No newline at end of file diff --git a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/three/csg.kt b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/three/csg.kt new file mode 100644 index 00000000..1e91e3f0 --- /dev/null +++ b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/three/csg.kt @@ -0,0 +1,95 @@ +@file:JsModule("@hi-level/three-csg") +@file:JsNonModule +@file:Suppress( + "INTERFACE_WITH_SUPERCLASS", + "OVERRIDING_FINAL_MEMBER", + "RETURN_TYPE_MISMATCH_ON_OVERRIDE", + "CONFLICTING_OVERLOADS", + "EXTERNAL_DELEGATION", + "NESTED_CLASS_IN_EXTERNAL_INTERFACE" +) + +package hep.dataforge.vis.spatial.three + + +import info.laht.threekt.math.Matrix4 +import info.laht.threekt.objects.Mesh + +open external class CSG { + open var polygons: Any + open fun clone(): CSG + open fun toPolygons(): Array + open fun union(csg: CSG): CSG + open fun subtract(csg: CSG): CSG + open fun intersect(csg: CSG): CSG + open fun inverse(): CSG + + companion object { + fun fromPolygons(polygons: Array): CSG + fun fromGeometry(geom: Any): CSG + fun fromMesh(mesh: Mesh): CSG + fun toMesh(csg: CSG, toMatrix: Matrix4): Mesh + fun iEval(tokens: Mesh, index: Number? = definedExternally /* null */): Unit + fun eval(tokens: Mesh, doRemove: Boolean): Mesh + var _tmpm3: Any + var doRemove: Any + var currentOp: Any + var currentPrim: Any + var nextPrim: Any + var sourceMesh: Any + } +} + +open external class Vector(x: Number, y: Number, z: Number) { + open fun clone(): Any + open fun negated(): Vector + open fun plus(a: Vector): Vector + open fun minus(a: Vector): Vector + open fun times(a: Number): Vector + open fun dividedBy(a: Number): Vector + open fun lerp(a: Vector, t: Number): Any + open fun unit(): Vector + open fun cross(a: Vector): Any +} + +external interface IVector { + var x: Number + var y: Number + var z: Number +} + +open external class Vertex(pos: IVector, normal: IVector, uv: IVector) { + open var pos: Vector + open var normal: Vector + open var uv: Vector + open fun clone(): Vertex + open fun flip(): Unit + open fun interpolate(other: Vertex, t: Number): Vertex +} + +open external class Plane(normal: Vector, w: Number) { + open var normal: Vector + open var w: Number + open fun clone(): Plane + open fun flip(): Unit + open fun splitPolygon( + polygon: Polygon, + coplanarFront: Array, + coplanarBack: Array, + front: Array, + back: Array + ): Unit + + companion object { + fun fromPoints(a: Vector, b: Vector, c: Vector): Plane + var EPSILON: Any + } +} + +open external class Polygon(vertices: Array, shared: Any? = definedExternally /* null */) { + open var plane: Plane + open var vertices: Array + open var shared: Any + open fun clone(): Polygon + open fun flip(): Unit +} \ No newline at end of file diff --git a/dataforge-vis-spatial-js/src/main/resources/JSRootGeoBase.js b/dataforge-vis-spatial-js/src/main/resources/JSRootGeoBase.js index b3be0eee..266a75ad 100644 --- a/dataforge-vis-spatial-js/src/main/resources/JSRootGeoBase.js +++ b/dataforge-vis-spatial-js/src/main/resources/JSRootGeoBase.js @@ -1,5 +1,5 @@ import * as JSROOT from "JSRootUtils" -import * as THREE from "three-full" +import * as THREE from "three" import * as ThreeBSP from "ThreeCSG" // Holder of all TGeo-related functions and classes diff --git a/dataforge-vis-spatial-js/src/main/resources/ThreeCSG.js b/dataforge-vis-spatial-js/src/main/resources/ThreeCSG.js index 9ec0707a..18e9f856 100644 --- a/dataforge-vis-spatial-js/src/main/resources/ThreeCSG.js +++ b/dataforge-vis-spatial-js/src/main/resources/ThreeCSG.js @@ -1,4 +1,4 @@ -import * as THREE from "three-full" +import * as THREE from "three" const EPSILON = 1e-5, COPLANAR = 0, diff --git a/dataforge-vis-spatial-js/src/main/web/index.html b/dataforge-vis-spatial-js/src/main/web/index.html index 8bab022b..ef65002b 100644 --- a/dataforge-vis-spatial-js/src/main/web/index.html +++ b/dataforge-vis-spatial-js/src/main/web/index.html @@ -22,9 +22,9 @@
- - - + diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Box.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Box.kt index bdca8ceb..64014656 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Box.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Box.kt @@ -26,12 +26,12 @@ class Box(parent: DisplayObject?, meta: Meta) : DisplayLeaf(parent, meta), Shape val node6 = Point3D(dx, -dy, dz) val node7 = Point3D(dx, dy, dz) val node8 = Point3D(-dx, dy, dz) - geometryBuilder.face4(node1, node4, node3, node2, Point3D(0, 0, -1)) - geometryBuilder.face4(node1, node2, node6, node5, Point3D(0, -1, 0)) - geometryBuilder.face4(node2, node3, node7, node6, Point3D(1, 0, 0)) - geometryBuilder.face4(node4, node8, node7, node3, Point3D(0, 1, 0)) - geometryBuilder.face4(node1, node5, node8, node4, Point3D(-1, 0, 0)) - geometryBuilder.face4(node8, node5, node6, node7, Point3D(0, 0, 1)) + geometryBuilder.face4(node1, node4, node3, node2) + geometryBuilder.face4(node1, node2, node6, node5) + geometryBuilder.face4(node2, node3, node7, node6) + geometryBuilder.face4(node4, node8, node7, node3) + geometryBuilder.face4(node1, node5, node8, node4) + geometryBuilder.face4(node8, node5, node6, node7) } companion object { @@ -40,4 +40,11 @@ class Box(parent: DisplayObject?, meta: Meta) : DisplayLeaf(parent, meta), Shape } fun DisplayGroup.box(meta: Meta = EmptyMeta, action: Box.() -> Unit = {}) = - Box(this, meta).apply(action).also { add(it) } \ No newline at end of file + Box(this, meta).apply(action).also { add(it) } + +fun DisplayGroup.box(xSize: Number, ySize: Number, zSize: Number, meta: Meta = EmptyMeta, action: Box.() -> Unit = {}) = + Box(this, meta).apply(action).apply{ + this.xSize = xSize.toDouble() + this.ySize = ySize.toDouble() + this.zSize = zSize.toDouble() + }.also { add(it) } \ No newline at end of file diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Composite.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Composite.kt index 7c176c6b..b38c47e5 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Composite.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Composite.kt @@ -2,12 +2,37 @@ package hep.dataforge.vis.spatial import hep.dataforge.meta.EmptyMeta import hep.dataforge.meta.Meta +import hep.dataforge.meta.seal +import hep.dataforge.vis.common.DisplayGroup import hep.dataforge.vis.common.DisplayLeaf import hep.dataforge.vis.common.DisplayObject +enum class CompositeType { + UNION, + INTERSECT, + SUBTRACT +} + class Composite( parent: DisplayObject?, val first: DisplayObject, val second: DisplayObject, + val type: CompositeType = CompositeType.UNION, meta: Meta = EmptyMeta -) : DisplayLeaf(parent,meta) \ No newline at end of file +) : DisplayLeaf(parent, meta) + +fun DisplayGroup.composite(type: CompositeType, builder: DisplayGroup.() -> Unit): Composite { + val group = DisplayGroup().apply(builder) + val children = group.toList() + if (children.size != 2) error("Composite requires exactly two children") + return Composite(this, children[0], children[1], type, group.properties.seal()).also { add(it) } +} + +fun DisplayGroup.union(builder: DisplayGroup.() -> Unit) = + composite(CompositeType.UNION,builder) + +fun DisplayGroup.subtract(builder: DisplayGroup.() -> Unit) = + composite(CompositeType.SUBTRACT,builder) + +fun DisplayGroup.intersect(builder: DisplayGroup.() -> Unit) = + composite(CompositeType.INTERSECT,builder) \ No newline at end of file diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/GeometryBuilder.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/GeometryBuilder.kt index e166e533..ddcf2ef5 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/GeometryBuilder.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/GeometryBuilder.kt @@ -27,6 +27,8 @@ data class Point3D(val x: Number, val y: Number, val z: Number) : MetaRepr { fun from(meta: Meta): Point3D{ return Point3D(meta["x"].number ?: 0, meta["y"].number ?: 0, meta["y"].number ?: 0) } + + val zero = Point3D(0,0,0) } } @@ -41,7 +43,7 @@ interface GeometryBuilder { * @param normal optional external normal to the face * @param meta optional additional platform-specific parameters like color or texture index */ - fun face(vertex1: Point3D, vertex2: Point3D, vertex3: Point3D, normal: Point3D? = null, meta: Meta = EmptyMeta) + fun face(vertex1: Point3D, vertex2: Point3D, vertex3: Point3D, normal: Point3D?, meta: Meta = EmptyMeta) fun build(): T }