diff --git a/build.gradle.kts b/build.gradle.kts index 7df9a46c..851ee471 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -14,6 +14,7 @@ allprojects { jcenter() maven("https://kotlin.bintray.com/kotlinx") maven("http://npm.mipt.ru:8081/artifactory/gradle-dev-local") + maven("https://kotlin.bintray.com/js-externals") } group = "hep.dataforge" 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 new file mode 100644 index 00000000..8ceb40d5 --- /dev/null +++ b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/DisplayGroup.kt @@ -0,0 +1,83 @@ +package hep.dataforge.vis.common + +import hep.dataforge.meta.EmptyMeta +import hep.dataforge.meta.Meta +import hep.dataforge.meta.Styled +import hep.dataforge.names.Name +import hep.dataforge.names.toName +import hep.dataforge.provider.Provider + +/** + * A display group which allows both named and unnamed children + */ +class DisplayGroup( + override val parent: DisplayObject? = null, meta: Meta = EmptyMeta +) : DisplayObject, Iterable, Provider { + + private val namedChildren = HashMap() + private val unnamedChildren = ArrayList() + + override val defaultTarget: String get() = DisplayObject.TARGET + 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 + } + } + + private data class Listener(val owner: Any?, val callback: (Name?, DisplayObject?) -> Unit) + + private val listeners = HashSet() + + /** + * Add listener for children change + */ + fun onChildrenChange(owner: Any?, action: (Name?, DisplayObject?) -> Unit) { + listeners.add(Listener(owner, action)) + } + + + /** + * Remove children change listener + */ + fun removeChildrenChangeListener(owner: Any?) { + listeners.removeAll { it.owner === owner } + } + + /** + * + */ + operator fun set(key: String, child: DisplayObject?) { + val name = key.toName() + if (child == null) { + namedChildren.remove(name) + } else { + namedChildren[name] = child + } + listeners.forEach { it.callback(name, child) } + } + + /** + * Append unnamed child + */ + fun add(child: DisplayObject) { + unnamedChildren.add(child) + listeners.forEach { it.callback(null, child) } + } + + /** + * remove unnamed child + */ + fun remove(child: DisplayObject) { + unnamedChildren.remove(child) + listeners.forEach { it.callback(null, null) } + } +} \ No newline at end of file diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/DisplayList.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/DisplayList.kt deleted file mode 100644 index 0e501d2d..00000000 --- a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/DisplayList.kt +++ /dev/null @@ -1,67 +0,0 @@ -package hep.dataforge.vis.common - -import hep.dataforge.meta.EmptyMeta -import hep.dataforge.meta.Meta -import hep.dataforge.meta.Styled - -internal data class InvalidationListener( - val owner: Any?, - val action: () -> Unit -) - -/** - * A [DisplayGroup] containing ordered list of elements - */ -class DisplayObjectList( - override val parent: DisplayObject? = null, -// override val type: String = DisplayObject.DEFAULT_TYPE, - meta: Meta = EmptyMeta -) : DisplayGroup { - private val _children = ArrayList() - - /** - * An ordered list of direct descendants - */ - val children: List get() = _children - - override fun iterator(): Iterator = children.iterator() - - - override val properties = Styled(meta) - private val listeners = HashSet() - - /** - * Add a child object and notify listeners - */ - fun addChild(obj: DisplayObject) { - _children.add(obj) - listeners.forEach { it.action() } - } - - - /** - * Remove a specific child and notify listeners - */ - fun removeChild(obj: DisplayObject) { - if (_children.remove(obj)) { - listeners.forEach { it.action } - } - } - - /** - * Add listener for children change - * TODO add detailed information into change listener - */ - fun onChildrenChange(owner: Any?, action: () -> Unit) { - listeners.add(InvalidationListener(owner, action)) - } - - - /** - * Remove children change listener - */ - fun removeChildrenChangeListener(owner: Any?) { - listeners.removeAll { it.owner === owner } - } -} - 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 8629af52..79952c4a 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 @@ -16,14 +16,11 @@ interface DisplayObject { */ 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 TARGET = "display" + const val DEFAULT_TYPE = "" //const val TYPE_KEY = "@type" //const val CHILDREN_KEY = "@children" @@ -73,4 +70,11 @@ open class DisplayLeaf( final override val properties = Styled(meta) } -interface DisplayGroup: DisplayObject, Iterable +///** +// * A group that could contain both named and unnamed children. Unnamed children could be accessed only via +// */ +//interface DisplayGroup : DisplayObject, Iterable, Provider { +// override val defaultTarget: String get() = DisplayObject.TARGET +// +// val children +//} diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/DisplayTree.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/DisplayTree.kt deleted file mode 100644 index f027d2cb..00000000 --- a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/DisplayTree.kt +++ /dev/null @@ -1,43 +0,0 @@ -package hep.dataforge.vis.common - -import hep.dataforge.names.Name -import hep.dataforge.names.NameToken - -/** - * A navigable hierarchical display node - */ -interface DisplayTree : DisplayGroup { - operator fun get(nameToken: NameToken): DisplayObject? -} - -interface MutableDisplayTree : DisplayTree { - operator fun set(nameToken: NameToken, group: DisplayObject) -} - -/** - * Recursively get a child - */ -tailrec operator fun DisplayTree.get(name: Name): DisplayObject? = when (name.length) { - 0 -> this - 1 -> this[name[0]] - else -> name.first()?.let { this[it] as? DisplayTree }?.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 MutableDisplayTree.set(name: Name, objFactory: (parent: DisplayObject) -> DisplayObject): Unit = - when (name.length) { - 0 -> error("Can't set object with empty name") - 1 -> set(name[0], objFactory(this)) - else -> (this[name.first()!!] ?: DisplayObjectList(this)).run { - if (this is MutableDisplayTree) { - 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/dataforge-vis-spatial-js/build.gradle.kts b/dataforge-vis-spatial-js/build.gradle.kts index 4693abbf..0c0767f5 100644 --- a/dataforge-vis-spatial-js/build.gradle.kts +++ b/dataforge-vis-spatial-js/build.gradle.kts @@ -9,7 +9,6 @@ plugins { id("org.jetbrains.kotlin.frontend") } - val kotlinVersion: String by rootProject.extra dependencies { @@ -24,6 +23,7 @@ configure { configure { dependency("three-full") + dependency("@hi-level/three-csg") dependency("style-loader") dependency("element-resize-event") devDependency("karma") 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/ThreeFactory.kt index b265d007..339864d5 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/ThreeFactory.kt @@ -1,13 +1,14 @@ package hep.dataforge.vis.spatial import hep.dataforge.meta.boolean +import hep.dataforge.names.startsWith +import hep.dataforge.names.toName 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.ThreeFactory.Companion.updateMesh import hep.dataforge.vis.spatial.three.ConvexBufferGeometry import hep.dataforge.vis.spatial.three.EdgesGeometry import hep.dataforge.vis.spatial.three.euler @@ -15,12 +16,11 @@ import info.laht.threekt.core.BufferGeometry import info.laht.threekt.core.Object3D import info.laht.threekt.geometries.BoxBufferGeometry import info.laht.threekt.geometries.WireframeGeometry -import info.laht.threekt.math.Vector3 import info.laht.threekt.objects.LineSegments import info.laht.threekt.objects.Mesh import kotlin.reflect.KClass -internal val DisplayObject.material get() = getProperty("color").material() +internal val DisplayObject.material get() = getProperty("material").material() /** * Builder and updater for three.js object @@ -35,39 +35,38 @@ interface ThreeFactory { companion object { const val TYPE = "threeFactory" - /** - * Update position, rotation and visibility - */ - internal fun updatePosition(obj: DisplayObject, target: Object3D) { - target.apply { - position.set(obj.x, obj.y, obj.z) - setRotationFromEuler(obj.euler) - scale.set(obj.scaleX, obj.scaleY, obj.scaleZ) - visible = obj.visible - } - } - internal fun buildMesh(obj: DisplayObject, geometry: BufferGeometry): Mesh { val mesh = Mesh(geometry, obj.material) - if (obj.getProperty("edges.enabled")?.boolean != false) { + + //inherited edges definition, enabled by default + if (obj.getProperty("edges.enabled").boolean != false) { val material = obj.getProperty("edges.material")?.material() ?: Materials.DEFAULT mesh.add(LineSegments(EdgesGeometry(mesh.geometry as BufferGeometry), material)) } - if (obj.getProperty("wireframe.enabled")?.boolean == true) { + //inherited wireframe definition, disabled by default + if (obj.getProperty("wireframe.enabled").boolean == true) { val material = obj.getProperty("edges.material")?.material() ?: Materials.DEFAULT mesh.add(LineSegments(WireframeGeometry(mesh.geometry as BufferGeometry), material)) } return mesh } - - internal fun updateMesh(obj: DisplayObject, geometry: BufferGeometry, mesh: Mesh) { - mesh.geometry = geometry - mesh.material = obj.material - } } } +/** + * Update position, rotation and visibility + */ +internal fun Object3D.updatePosition(obj: DisplayObject) { + position.set(obj.x, obj.y, obj.z) + setRotationFromEuler(obj.euler) + scale.set(obj.scaleX, obj.scaleY, obj.scaleZ) + visible = obj.visible +} + +/** + * Unsafe invocation of a factory + */ operator fun ThreeFactory.invoke(obj: Any): Object3D { if (type.isInstance(obj)) { return invoke(obj as T) @@ -76,9 +75,12 @@ operator fun ThreeFactory.invoke(obj: Any): Object3D { } } +/** + * Basic geometry-based factory + */ abstract class MeshThreeFactory(override val type: KClass) : ThreeFactory { /** - * Build an object + * Build a geometry for an object */ abstract fun buildGeometry(obj: T): BufferGeometry @@ -86,13 +88,33 @@ abstract class MeshThreeFactory(override val type: KClass - ThreeFactory.updatePosition(obj, mesh) - updateMesh(obj, buildGeometry(obj), mesh) + + //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 = buildGeometry(obj) + mesh.material = obj.material + } } return mesh } @@ -104,7 +126,7 @@ abstract class MeshThreeFactory(override val type: KClass(Shape::class) { override fun buildGeometry(obj: Shape): BufferGeometry { return obj.run { - ThreeGeometryBuilder().apply { buildGeometry() }.build() + ThreeGeometryBuilder().apply { toGeometry(this) }.build() } } } @@ -114,8 +136,7 @@ object ThreeBoxFactory : MeshThreeFactory(Box::class) { BoxBufferGeometry(obj.xSize, obj.ySize, obj.zSize) } -fun Point3D.asVector(): Vector3 = Vector3(this.x, this.y, this.z) - +//FIXME not functional yet object ThreeConvexFactory : MeshThreeFactory(Convex::class) { override fun buildGeometry(obj: Convex): ConvexBufferGeometry { val vectors = obj.points.map { it.asVector() }.toTypedArray() 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/ThreeGeometryBuilder.kt index a22e8846..699da72d 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/ThreeGeometryBuilder.kt @@ -10,6 +10,9 @@ import info.laht.threekt.core.Geometry import info.laht.threekt.math.Color import info.laht.threekt.math.Vector3 +// TODO use unsafe cast instead +fun Point3D.asVector(): Vector3 = Vector3(this.x, this.y, this.z) + class ThreeGeometryBuilder : GeometryBuilder { private val vertices = ArrayList() 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 index e40c306a..25f08f89 100644 --- 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 @@ -73,9 +73,12 @@ class ThreeOutput(override val context: Context, val meta: Meta = EmptyMeta) : O } private fun buildNode(obj: DisplayObject): Object3D? { - return if (obj is DisplayGroup) Group(obj.mapNotNull { buildNode(it) }).apply { - ThreeFactory.updatePosition(obj, this) + 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) @@ -94,6 +97,6 @@ class ThreeOutput(override val context: Context, val meta: Meta = EmptyMeta) : O companion object { fun build(context: Context, meta: Meta = EmptyMeta, override: MetaBuilder.() -> Unit) = - ThreeOutput(context, buildMeta(meta,override)) + 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 index 1f73073d..11aeba52 100644 --- 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 @@ -5,7 +5,7 @@ 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.names.set class ThreePlugin : AbstractPlugin() { override val tag: PluginTag get() = ThreePlugin.tag @@ -13,8 +13,8 @@ class ThreePlugin : AbstractPlugin() { val factories = HashMap>() init { - //factories["box".toName()] = ThreeBoxFactory - factories["convex".toName()] = ThreeConvexFactory + factories["box"] = ThreeBoxFactory + factories["convex"] = ThreeConvexFactory } override fun listNames(target: String): Sequence { 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 4abf0938..b2e1e1f1 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 @@ -27,7 +27,7 @@ class ThreeDemoApp : ApplicationBase() { plugin(JSRootPlugin()) }.build() - val grid = context.plugins.load(ThreeDemoGrid()).apply { + context.plugins.load(ThreeDemoGrid()).run { demo("group", "Group demo") { val group = group { box { @@ -77,8 +77,8 @@ class ThreeDemoApp : ApplicationBase() { shape { polygon(8, 50) } - for(i in 0..100) { - layer(i*5, 20*sin(2*PI/100*i), 20*cos(2*PI/100*i)) + for (i in 0..100) { + layer(i * 5, 20 * sin(2 * PI / 100 * i), 20 * cos(2 * PI / 100 * i)) } } } 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 771cb0ab..68b2da9a 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 @@ -12,8 +12,8 @@ import hep.dataforge.names.Name import hep.dataforge.names.toName 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.common.DisplayObjectList import hep.dataforge.vis.spatial.ThreeOutput import hep.dataforge.vis.spatial.render import kotlinx.html.dom.append @@ -24,6 +24,7 @@ import kotlinx.html.id import kotlinx.html.js.div import kotlinx.html.span import kotlin.browser.document +import kotlin.dom.clear import kotlin.reflect.KClass class ThreeDemoGrid(meta: Meta) : AbstractPlugin(meta), OutputManager { @@ -36,6 +37,7 @@ class ThreeDemoGrid(meta: Meta) : AbstractPlugin(meta), OutputManager { super.attach(context) val elementId = meta["elementID"].string ?: "canvas" val element = document.getElementById(elementId) ?: error("Element with id $elementId not found on page") + element.clear() element.append(gridRoot) } @@ -47,10 +49,11 @@ class ThreeDemoGrid(meta: Meta) : AbstractPlugin(meta), OutputManager { "size" to 500 } } + //TODO calculate cell width here using jquery gridRoot.append { span("border") { div("col-4") { - output.attach(div { id = "output-$name" }){300} + output.attach(div { id = "output-$name" }){ 300} hr() h2 { +(meta["title"].string ?: name.toString()) } } @@ -70,7 +73,7 @@ class ThreeDemoGrid(meta: Meta) : AbstractPlugin(meta), OutputManager { } } -fun ThreeDemoGrid.demo(name: String, title: String = name, block: DisplayObjectList.() -> Unit) { +fun ThreeDemoGrid.demo(name: String, title: String = name, block: DisplayGroup.() -> Unit) { val meta = buildMeta { "title" to title } 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 39575303..b4b64467 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 @@ -67,7 +67,7 @@ class JSRootDemoApp : ApplicationBase() { renderer.render { val json = parse(string) - JSRootObject(this, EmptyMeta, json).also { addChild(it) } + JSRootObject(this, EmptyMeta, json).also { add(it) } } } readAsText(file) 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 de322e22..54e3a0ef 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 @@ -4,7 +4,6 @@ import hep.dataforge.meta.EmptyMeta import hep.dataforge.meta.Meta import hep.dataforge.meta.buildMeta import hep.dataforge.meta.toDynamic -import hep.dataforge.vis.* import hep.dataforge.vis.common.* import hep.dataforge.vis.spatial.MeshThreeFactory import info.laht.threekt.core.BufferGeometry @@ -54,8 +53,8 @@ class JSRootGeometry(parent: DisplayObject?, meta: Meta) : DisplayLeaf(parent, m } } -fun DisplayObjectList.jsRootGeometry(meta: Meta = EmptyMeta, action: JSRootGeometry.() -> Unit = {}) = - JSRootGeometry(this, meta).apply(action).also { addChild(it) } +fun DisplayGroup.jsRootGeometry(meta: Meta = EmptyMeta, action: JSRootGeometry.() -> Unit = {}) = + JSRootGeometry(this, meta).apply(action).also { add(it) } //fun Meta.toDynamic(): dynamic { // fun MetaItem<*>.toDynamic(): dynamic = when (this) { 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 700c17d6..a62f6f61 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 @@ -3,9 +3,9 @@ package hep.dataforge.vis.spatial.jsroot import hep.dataforge.meta.EmptyMeta import hep.dataforge.meta.Meta import hep.dataforge.meta.toDynamic +import hep.dataforge.vis.common.DisplayGroup import hep.dataforge.vis.common.DisplayLeaf import hep.dataforge.vis.common.DisplayObject -import hep.dataforge.vis.common.DisplayObjectList import hep.dataforge.vis.common.node import hep.dataforge.vis.spatial.ThreeFactory import info.laht.threekt.core.Object3D @@ -28,7 +28,7 @@ object ThreeJSRootObjectFactory : ThreeFactory { } } -fun DisplayObjectList.jsRootObject(str: String) { +fun DisplayGroup.jsRootObject(str: String) { val json = JSON.parse(str) - JSRootObject(this, EmptyMeta, json).also { addChild(it) } + JSRootObject(this, EmptyMeta, json).also { add(it) } } \ No newline at end of file diff --git a/dataforge-vis-spatial-js/src/main/web/index.html b/dataforge-vis-spatial-js/src/main/web/index.html index 35a7d75b..8bab022b 100644 --- a/dataforge-vis-spatial-js/src/main/web/index.html +++ b/dataforge-vis-spatial-js/src/main/web/index.html @@ -4,18 +4,6 @@ Three js demo for particle physics - - - - - - - - - - - - @@ -34,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 dfe57903..bdca8ceb 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 @@ -2,9 +2,9 @@ package hep.dataforge.vis.spatial import hep.dataforge.meta.EmptyMeta import hep.dataforge.meta.Meta +import hep.dataforge.vis.common.DisplayGroup import hep.dataforge.vis.common.DisplayLeaf import hep.dataforge.vis.common.DisplayObject -import hep.dataforge.vis.common.DisplayObjectList import hep.dataforge.vis.common.double class Box(parent: DisplayObject?, meta: Meta) : DisplayLeaf(parent, meta), Shape { @@ -14,7 +14,7 @@ class Box(parent: DisplayObject?, meta: Meta) : DisplayLeaf(parent, meta), Shape //TODO add helper for color configuration - override fun GeometryBuilder.buildGeometry() { + override fun toGeometry(geometryBuilder: GeometryBuilder) { val dx = xSize / 2 val dy = ySize / 2 val dz = zSize / 2 @@ -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) - face4(node1, node4, node3, node2, Point3D(0, 0, -1)) - face4(node1, node2, node6, node5, Point3D(0, -1, 0)) - face4(node2, node3, node7, node6, Point3D(1, 0, 0)) - face4(node4, node8, node7, node3, Point3D(0, 1, 0)) - face4(node1, node5, node8, node4, Point3D(-1, 0, 0)) - face4(node8, node5, node6, node7, Point3D(0, 0, 1)) + 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)) } companion object { @@ -39,5 +39,5 @@ class Box(parent: DisplayObject?, meta: Meta) : DisplayLeaf(parent, meta), Shape } } -fun DisplayObjectList.box(meta: Meta = EmptyMeta, action: Box.() -> Unit = {}) = - Box(this, meta).apply(action).also { addChild(it) } \ No newline at end of file +fun DisplayGroup.box(meta: Meta = EmptyMeta, action: Box.() -> Unit = {}) = + Box(this, meta).apply(action).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 new file mode 100644 index 00000000..7c176c6b --- /dev/null +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Composite.kt @@ -0,0 +1,13 @@ +package hep.dataforge.vis.spatial + +import hep.dataforge.meta.EmptyMeta +import hep.dataforge.meta.Meta +import hep.dataforge.vis.common.DisplayLeaf +import hep.dataforge.vis.common.DisplayObject + +class Composite( + parent: DisplayObject?, + val first: DisplayObject, + val second: DisplayObject, + meta: Meta = EmptyMeta +) : DisplayLeaf(parent,meta) \ No newline at end of file diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Convex.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Convex.kt index 8d92d3f4..ad2f7e12 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Convex.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Convex.kt @@ -1,10 +1,9 @@ package hep.dataforge.vis.spatial import hep.dataforge.meta.* -import hep.dataforge.names.toName +import hep.dataforge.vis.common.DisplayGroup import hep.dataforge.vis.common.DisplayLeaf import hep.dataforge.vis.common.DisplayObject -import hep.dataforge.vis.common.DisplayObjectList class Convex(parent: DisplayObject?, meta: Meta) : DisplayLeaf(parent, meta) { @@ -14,15 +13,15 @@ class Convex(parent: DisplayObject?, meta: Meta) : DisplayLeaf(parent, meta) { const val TYPE = "geometry.3d.convex" fun points(item: MetaItem<*>): List { - return item.node?.getAll("point".toName())?.map { (_, value) -> - Point3D(value.node["x"].number ?: 0, value.node["y"].number ?: 0, value.node["y"].number ?: 0) + return item.node?.getAll("point")?.map { (_, value) -> + Point3D.from(value.node?: error("Point definition is not a node")) } ?: emptyList() } } } -fun DisplayObjectList.convex(meta: Meta = EmptyMeta, action: ConvexBuilder.() -> Unit = {}) = - ConvexBuilder().apply(action).build(this, meta).also { addChild(it) } +fun DisplayGroup.convex(meta: Meta = EmptyMeta, action: ConvexBuilder.() -> Unit = {}) = + ConvexBuilder().apply(action).build(this, meta).also { add(it) } class ConvexBuilder { private val points = ArrayList() diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Extruded.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Extruded.kt index 78d7569b..b4c552a7 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Extruded.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Extruded.kt @@ -1,9 +1,9 @@ package hep.dataforge.vis.spatial import hep.dataforge.meta.* +import hep.dataforge.vis.common.DisplayGroup import hep.dataforge.vis.common.DisplayLeaf import hep.dataforge.vis.common.DisplayObject -import hep.dataforge.vis.common.DisplayObjectList import kotlin.math.PI import kotlin.math.cos import kotlin.math.sin @@ -46,7 +46,7 @@ class Extruded(parent: DisplayObject?, meta: Meta) : DisplayLeaf(parent, meta), val shape get() = properties.getAll("shape.point").map { (_, value) -> - Point2D(value.node["x"].number ?: 0, value.node["y"].number ?: 0) + Point2D.from(value.node ?: error("Point definition is not a node")) } fun shape(block: Shape2DBuilder.() -> Unit) { @@ -70,7 +70,7 @@ class Extruded(parent: DisplayObject?, meta: Meta) : DisplayLeaf(parent, meta), return layer } - override fun GeometryBuilder.buildGeometry() { + override fun toGeometry(geometryBuilder: GeometryBuilder) { val shape: Shape2D = shape if (shape.size < 3) error("Extruded shape requires more than points per layer") @@ -95,7 +95,7 @@ class Extruded(parent: DisplayObject?, meta: Meta) : DisplayLeaf(parent, meta), upperLayer = layers[i] for (j in (0 until shape.size - 1)) { //counter clockwise - face4( + geometryBuilder.face4( lowerLayer[j], lowerLayer[j + 1], upperLayer[j + 1], @@ -104,7 +104,7 @@ class Extruded(parent: DisplayObject?, meta: Meta) : DisplayLeaf(parent, meta), } // final face - face4( + geometryBuilder.face4( lowerLayer[shape.size - 1], lowerLayer[0], upperLayer[0], @@ -119,5 +119,5 @@ class Extruded(parent: DisplayObject?, meta: Meta) : DisplayLeaf(parent, meta), } } -fun DisplayObjectList.extrude(meta: Meta = EmptyMeta, action: Extruded.() -> Unit = {}) = - Extruded(this, meta).apply(action).also { addChild(it) } \ No newline at end of file +fun DisplayGroup.extrude(meta: Meta = EmptyMeta, action: Extruded.() -> Unit = {}) = + Extruded(this, meta).apply(action).also { add(it) } \ 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 36a2ddc6..e166e533 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 @@ -1,16 +1,19 @@ package hep.dataforge.vis.spatial -import hep.dataforge.meta.EmptyMeta -import hep.dataforge.meta.Meta -import hep.dataforge.meta.MetaRepr -import hep.dataforge.meta.buildMeta +import hep.dataforge.meta.* import hep.dataforge.vis.common.DisplayObject -data class Point2D(val x: Number, val y: Number): MetaRepr{ +data class Point2D(val x: Number, val y: Number) : MetaRepr { override fun toMeta(): Meta = buildMeta { "x" to x "y" to y } + + companion object{ + fun from(meta: Meta): Point2D{ + return Point2D(meta["x"].number ?: 0, meta["y"].number ?: 0) + } + } } data class Point3D(val x: Number, val y: Number, val z: Number) : MetaRepr { @@ -19,12 +22,18 @@ data class Point3D(val x: Number, val y: Number, val z: Number) : MetaRepr { "y" to y "z" to z } + + companion object{ + fun from(meta: Meta): Point3D{ + return Point3D(meta["x"].number ?: 0, meta["y"].number ?: 0, meta["y"].number ?: 0) + } + } } /** * @param T the type of resulting geometry */ -interface GeometryBuilder { +interface GeometryBuilder { /** * Add a face to 3D model. If one of the vertices is not present in the current geometry model list of vertices, * it is added automatically. @@ -35,7 +44,6 @@ interface GeometryBuilder { fun face(vertex1: Point3D, vertex2: Point3D, vertex3: Point3D, normal: Point3D? = null, meta: Meta = EmptyMeta) fun build(): T - } fun GeometryBuilder<*>.face4( @@ -50,6 +58,6 @@ fun GeometryBuilder<*>.face4( face(vertex1, vertex3, vertex4, normal, meta) } -interface Shape: DisplayObject { - fun GeometryBuilder.buildGeometry() +interface Shape : DisplayObject { + fun toGeometry(geometryBuilder: GeometryBuilder) } \ No newline at end of file diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/displayObject3D.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/displayObject3D.kt index 227bb70c..147fe952 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/displayObject3D.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/displayObject3D.kt @@ -4,15 +4,14 @@ 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.common.DisplayObjectList import hep.dataforge.vis.common.getProperty -fun DisplayObjectList.group(meta: Meta = EmptyMeta, action: DisplayObjectList.() -> Unit = {}): DisplayGroup = - DisplayObjectList(this, meta).apply(action).also { addChild(it) } +fun DisplayGroup.group(meta: Meta = EmptyMeta, action: DisplayGroup.() -> Unit = {}): DisplayGroup = + DisplayGroup(this, meta).apply(action).also { add(it) } -fun Output.render(meta: Meta = EmptyMeta, action: DisplayObjectList.() -> Unit) = - render(DisplayObjectList(null, EmptyMeta).apply(action), meta) +fun Output.render(meta: Meta = EmptyMeta, action: DisplayGroup.() -> Unit) = + render(DisplayGroup(null, EmptyMeta).apply(action), meta) //TODO replace properties by containers? @@ -133,11 +132,11 @@ var DisplayObject.scaleZ } fun DisplayObject.color(rgb: Int) { - this.properties["color"] = rgb + this.properties["material"] = rgb } fun DisplayObject.color(meta: Meta) { - this.properties["color"] = meta + this.properties["material"] = meta } fun DisplayObject.color(r: Int, g: Int, b: Int) = color(buildMeta { diff --git a/dataforge-vis-spatial/src/commonTest/kotlin/hep/dataforge/vis/spatial/ConvexTest.kt b/dataforge-vis-spatial/src/commonTest/kotlin/hep/dataforge/vis/spatial/ConvexTest.kt index eb78bfb9..bd375a72 100644 --- a/dataforge-vis-spatial/src/commonTest/kotlin/hep/dataforge/vis/spatial/ConvexTest.kt +++ b/dataforge-vis-spatial/src/commonTest/kotlin/hep/dataforge/vis/spatial/ConvexTest.kt @@ -4,14 +4,14 @@ import hep.dataforge.meta.get import hep.dataforge.meta.getAll import hep.dataforge.meta.node import hep.dataforge.names.toName -import hep.dataforge.vis.common.DisplayObjectList +import hep.dataforge.vis.common.DisplayGroup import kotlin.test.Test import kotlin.test.assertEquals class ConvexTest { @Test fun testConvexBuilder() { - val group = DisplayObjectList().apply { + val group = DisplayGroup().apply { convex { point(50, 50, -50) point(50, -50, -50) @@ -24,7 +24,7 @@ class ConvexTest { } } - val convex = group.children.first() as Convex + val convex = group.first() as Convex val pointsNode = convex.properties["points"].node