A major change in DisplayGroup implementation

This commit is contained in:
Alexander Nozik 2019-07-03 16:02:26 +03:00
parent 4274dfff88
commit 697e7908a7
23 changed files with 237 additions and 223 deletions

View File

@ -14,6 +14,7 @@ allprojects {
jcenter() jcenter()
maven("https://kotlin.bintray.com/kotlinx") maven("https://kotlin.bintray.com/kotlinx")
maven("http://npm.mipt.ru:8081/artifactory/gradle-dev-local") maven("http://npm.mipt.ru:8081/artifactory/gradle-dev-local")
maven("https://kotlin.bintray.com/js-externals")
} }
group = "hep.dataforge" group = "hep.dataforge"

View File

@ -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<DisplayObject>, Provider {
private val namedChildren = HashMap<Name, DisplayObject>()
private val unnamedChildren = ArrayList<DisplayObject>()
override val defaultTarget: String get() = DisplayObject.TARGET
override val properties: Styled = Styled(meta)
override fun iterator(): Iterator<DisplayObject> = (namedChildren.values + unnamedChildren).iterator()
override fun listNames(target: String): Sequence<Name> =
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<Listener>()
/**
* 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) }
}
}

View File

@ -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<DisplayObject>()
/**
* An ordered list of direct descendants
*/
val children: List<DisplayObject> get() = _children
override fun iterator(): Iterator<DisplayObject> = children.iterator()
override val properties = Styled(meta)
private val listeners = HashSet<InvalidationListener>()
/**
* 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 }
}
}

View File

@ -16,14 +16,11 @@ interface DisplayObject {
*/ */
val parent: DisplayObject? val parent: DisplayObject?
// /**
// * The type of this object. Uses `.` notation. Empty type means untyped group
// */
// val type: String
val properties: Styled val properties: Styled
companion object { companion object {
const val TARGET = "display"
const val DEFAULT_TYPE = "" const val DEFAULT_TYPE = ""
//const val TYPE_KEY = "@type" //const val TYPE_KEY = "@type"
//const val CHILDREN_KEY = "@children" //const val CHILDREN_KEY = "@children"
@ -73,4 +70,11 @@ open class DisplayLeaf(
final override val properties = Styled(meta) final override val properties = Styled(meta)
} }
interface DisplayGroup: DisplayObject, Iterable<DisplayObject> ///**
// * A group that could contain both named and unnamed children. Unnamed children could be accessed only via
// */
//interface DisplayGroup : DisplayObject, Iterable<DisplayObject>, Provider {
// override val defaultTarget: String get() = DisplayObject.TARGET
//
// val children
//}

View File

@ -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")
}
}
}

View File

@ -9,7 +9,6 @@ plugins {
id("org.jetbrains.kotlin.frontend") id("org.jetbrains.kotlin.frontend")
} }
val kotlinVersion: String by rootProject.extra val kotlinVersion: String by rootProject.extra
dependencies { dependencies {
@ -24,6 +23,7 @@ configure<KotlinFrontendExtension> {
configure<NpmExtension> { configure<NpmExtension> {
dependency("three-full") dependency("three-full")
dependency("@hi-level/three-csg")
dependency("style-loader") dependency("style-loader")
dependency("element-resize-event") dependency("element-resize-event")
devDependency("karma") devDependency("karma")

View File

@ -1,13 +1,14 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.meta.boolean import hep.dataforge.meta.boolean
import hep.dataforge.names.startsWith
import hep.dataforge.names.toName
import hep.dataforge.provider.Type import hep.dataforge.provider.Type
import hep.dataforge.vis.common.DisplayObject import hep.dataforge.vis.common.DisplayObject
import hep.dataforge.vis.common.getProperty import hep.dataforge.vis.common.getProperty
import hep.dataforge.vis.common.onChange import hep.dataforge.vis.common.onChange
import hep.dataforge.vis.spatial.ThreeFactory.Companion.TYPE import hep.dataforge.vis.spatial.ThreeFactory.Companion.TYPE
import hep.dataforge.vis.spatial.ThreeFactory.Companion.buildMesh 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.ConvexBufferGeometry
import hep.dataforge.vis.spatial.three.EdgesGeometry import hep.dataforge.vis.spatial.three.EdgesGeometry
import hep.dataforge.vis.spatial.three.euler 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.core.Object3D
import info.laht.threekt.geometries.BoxBufferGeometry import info.laht.threekt.geometries.BoxBufferGeometry
import info.laht.threekt.geometries.WireframeGeometry import info.laht.threekt.geometries.WireframeGeometry
import info.laht.threekt.math.Vector3
import info.laht.threekt.objects.LineSegments import info.laht.threekt.objects.LineSegments
import info.laht.threekt.objects.Mesh import info.laht.threekt.objects.Mesh
import kotlin.reflect.KClass 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 * Builder and updater for three.js object
@ -35,39 +35,38 @@ interface ThreeFactory<T : DisplayObject> {
companion object { companion object {
const val TYPE = "threeFactory" 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 { internal fun buildMesh(obj: DisplayObject, geometry: BufferGeometry): Mesh {
val mesh = Mesh(geometry, obj.material) 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 val material = obj.getProperty("edges.material")?.material() ?: Materials.DEFAULT
mesh.add(LineSegments(EdgesGeometry(mesh.geometry as BufferGeometry), material)) 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 val material = obj.getProperty("edges.material")?.material() ?: Materials.DEFAULT
mesh.add(LineSegments(WireframeGeometry(mesh.geometry as BufferGeometry), material)) mesh.add(LineSegments(WireframeGeometry(mesh.geometry as BufferGeometry), material))
} }
return mesh 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 <T : DisplayObject> ThreeFactory<T>.invoke(obj: Any): Object3D { operator fun <T : DisplayObject> ThreeFactory<T>.invoke(obj: Any): Object3D {
if (type.isInstance(obj)) { if (type.isInstance(obj)) {
return invoke(obj as T) return invoke(obj as T)
@ -76,9 +75,12 @@ operator fun <T : DisplayObject> ThreeFactory<T>.invoke(obj: Any): Object3D {
} }
} }
/**
* Basic geometry-based factory
*/
abstract class MeshThreeFactory<T : DisplayObject>(override val type: KClass<out T>) : ThreeFactory<T> { abstract class MeshThreeFactory<T : DisplayObject>(override val type: KClass<out T>) : ThreeFactory<T> {
/** /**
* Build an object * Build a geometry for an object
*/ */
abstract fun buildGeometry(obj: T): BufferGeometry abstract fun buildGeometry(obj: T): BufferGeometry
@ -86,13 +88,33 @@ abstract class MeshThreeFactory<T : DisplayObject>(override val type: KClass<out
override fun invoke(obj: T): Mesh { override fun invoke(obj: T): Mesh {
val geometry = buildGeometry(obj) val geometry = buildGeometry(obj)
//JS sometimes tries to pass Geometry as BufferGeometry
@Suppress("USELESS_IS_CHECK") if (geometry !is BufferGeometry) error("BufferGeometry expected") @Suppress("USELESS_IS_CHECK") if (geometry !is BufferGeometry) error("BufferGeometry expected")
//create mesh from geometry
val mesh = buildMesh(obj, geometry) val mesh = buildMesh(obj, geometry)
ThreeFactory.updatePosition(obj, mesh)
obj.onChange(this) { _, _, _ -> //set position for meseh
ThreeFactory.updatePosition(obj, mesh) mesh.updatePosition(obj)
updateMesh(obj, buildGeometry(obj), mesh)
//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 return mesh
} }
@ -104,7 +126,7 @@ abstract class MeshThreeFactory<T : DisplayObject>(override val type: KClass<out
object ThreeShapeFactory : MeshThreeFactory<Shape>(Shape::class) { object ThreeShapeFactory : MeshThreeFactory<Shape>(Shape::class) {
override fun buildGeometry(obj: Shape): BufferGeometry { override fun buildGeometry(obj: Shape): BufferGeometry {
return obj.run { return obj.run {
ThreeGeometryBuilder().apply { buildGeometry() }.build() ThreeGeometryBuilder().apply { toGeometry(this) }.build()
} }
} }
} }
@ -114,8 +136,7 @@ object ThreeBoxFactory : MeshThreeFactory<Box>(Box::class) {
BoxBufferGeometry(obj.xSize, obj.ySize, obj.zSize) 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>(Convex::class) { object ThreeConvexFactory : MeshThreeFactory<Convex>(Convex::class) {
override fun buildGeometry(obj: Convex): ConvexBufferGeometry { override fun buildGeometry(obj: Convex): ConvexBufferGeometry {
val vectors = obj.points.map { it.asVector() }.toTypedArray() val vectors = obj.points.map { it.asVector() }.toTypedArray()

View File

@ -10,6 +10,9 @@ import info.laht.threekt.core.Geometry
import info.laht.threekt.math.Color import info.laht.threekt.math.Color
import info.laht.threekt.math.Vector3 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<BufferGeometry> { class ThreeGeometryBuilder : GeometryBuilder<BufferGeometry> {
private val vertices = ArrayList<Point3D>() private val vertices = ArrayList<Point3D>()

View File

@ -73,9 +73,12 @@ class ThreeOutput(override val context: Context, val meta: Meta = EmptyMeta) : O
} }
private fun buildNode(obj: DisplayObject): Object3D? { private fun buildNode(obj: DisplayObject): Object3D? {
return if (obj is DisplayGroup) Group(obj.mapNotNull { buildNode(it) }).apply { return if (obj is DisplayGroup) {
ThreeFactory.updatePosition(obj, this) Group(obj.mapNotNull { buildNode(it) }).apply {
updatePosition(obj)
}
} else { } else {
//find specialized factory for this type if it is present
val factory = context.content<ThreeFactory<*>>(ThreeFactory.TYPE).values.find { it.type == obj::class } val factory = context.content<ThreeFactory<*>>(ThreeFactory.TYPE).values.find { it.type == obj::class }
when { when {
factory != null -> factory(obj) factory != null -> factory(obj)
@ -94,6 +97,6 @@ class ThreeOutput(override val context: Context, val meta: Meta = EmptyMeta) : O
companion object { companion object {
fun build(context: Context, meta: Meta = EmptyMeta, override: MetaBuilder.() -> Unit) = fun build(context: Context, meta: Meta = EmptyMeta, override: MetaBuilder.() -> Unit) =
ThreeOutput(context, buildMeta(meta,override)) ThreeOutput(context, buildMeta(meta, override))
} }
} }

View File

@ -5,7 +5,7 @@ import hep.dataforge.context.PluginFactory
import hep.dataforge.context.PluginTag import hep.dataforge.context.PluginTag
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.toName import hep.dataforge.names.set
class ThreePlugin : AbstractPlugin() { class ThreePlugin : AbstractPlugin() {
override val tag: PluginTag get() = ThreePlugin.tag override val tag: PluginTag get() = ThreePlugin.tag
@ -13,8 +13,8 @@ class ThreePlugin : AbstractPlugin() {
val factories = HashMap<Name, ThreeFactory<*>>() val factories = HashMap<Name, ThreeFactory<*>>()
init { init {
//factories["box".toName()] = ThreeBoxFactory factories["box"] = ThreeBoxFactory
factories["convex".toName()] = ThreeConvexFactory factories["convex"] = ThreeConvexFactory
} }
override fun listNames(target: String): Sequence<Name> { override fun listNames(target: String): Sequence<Name> {

View File

@ -27,7 +27,7 @@ class ThreeDemoApp : ApplicationBase() {
plugin(JSRootPlugin()) plugin(JSRootPlugin())
}.build() }.build()
val grid = context.plugins.load(ThreeDemoGrid()).apply { context.plugins.load(ThreeDemoGrid()).run {
demo("group", "Group demo") { demo("group", "Group demo") {
val group = group { val group = group {
box { box {
@ -77,8 +77,8 @@ class ThreeDemoApp : ApplicationBase() {
shape { shape {
polygon(8, 50) polygon(8, 50)
} }
for(i in 0..100) { for (i in 0..100) {
layer(i*5, 20*sin(2*PI/100*i), 20*cos(2*PI/100*i)) layer(i * 5, 20 * sin(2 * PI / 100 * i), 20 * cos(2 * PI / 100 * i))
} }
} }
} }

View File

@ -12,8 +12,8 @@ import hep.dataforge.names.Name
import hep.dataforge.names.toName import hep.dataforge.names.toName
import hep.dataforge.output.Output import hep.dataforge.output.Output
import hep.dataforge.output.OutputManager import hep.dataforge.output.OutputManager
import hep.dataforge.vis.common.DisplayGroup
import hep.dataforge.vis.common.DisplayObject import hep.dataforge.vis.common.DisplayObject
import hep.dataforge.vis.common.DisplayObjectList
import hep.dataforge.vis.spatial.ThreeOutput import hep.dataforge.vis.spatial.ThreeOutput
import hep.dataforge.vis.spatial.render import hep.dataforge.vis.spatial.render
import kotlinx.html.dom.append import kotlinx.html.dom.append
@ -24,6 +24,7 @@ import kotlinx.html.id
import kotlinx.html.js.div import kotlinx.html.js.div
import kotlinx.html.span import kotlinx.html.span
import kotlin.browser.document import kotlin.browser.document
import kotlin.dom.clear
import kotlin.reflect.KClass import kotlin.reflect.KClass
class ThreeDemoGrid(meta: Meta) : AbstractPlugin(meta), OutputManager { class ThreeDemoGrid(meta: Meta) : AbstractPlugin(meta), OutputManager {
@ -36,6 +37,7 @@ class ThreeDemoGrid(meta: Meta) : AbstractPlugin(meta), OutputManager {
super.attach(context) super.attach(context)
val elementId = meta["elementID"].string ?: "canvas" val elementId = meta["elementID"].string ?: "canvas"
val element = document.getElementById(elementId) ?: error("Element with id $elementId not found on page") val element = document.getElementById(elementId) ?: error("Element with id $elementId not found on page")
element.clear()
element.append(gridRoot) element.append(gridRoot)
} }
@ -47,10 +49,11 @@ class ThreeDemoGrid(meta: Meta) : AbstractPlugin(meta), OutputManager {
"size" to 500 "size" to 500
} }
} }
//TODO calculate cell width here using jquery
gridRoot.append { gridRoot.append {
span("border") { span("border") {
div("col-4") { div("col-4") {
output.attach(div { id = "output-$name" }){300} output.attach(div { id = "output-$name" }){ 300}
hr() hr()
h2 { +(meta["title"].string ?: name.toString()) } 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 { val meta = buildMeta {
"title" to title "title" to title
} }

View File

@ -67,7 +67,7 @@ class JSRootDemoApp : ApplicationBase() {
renderer.render { renderer.render {
val json = parse(string) val json = parse(string)
JSRootObject(this, EmptyMeta, json).also { addChild(it) } JSRootObject(this, EmptyMeta, json).also { add(it) }
} }
} }
readAsText(file) readAsText(file)

View File

@ -4,7 +4,6 @@ import hep.dataforge.meta.EmptyMeta
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.meta.buildMeta import hep.dataforge.meta.buildMeta
import hep.dataforge.meta.toDynamic import hep.dataforge.meta.toDynamic
import hep.dataforge.vis.*
import hep.dataforge.vis.common.* import hep.dataforge.vis.common.*
import hep.dataforge.vis.spatial.MeshThreeFactory import hep.dataforge.vis.spatial.MeshThreeFactory
import info.laht.threekt.core.BufferGeometry 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 = {}) = fun DisplayGroup.jsRootGeometry(meta: Meta = EmptyMeta, action: JSRootGeometry.() -> Unit = {}) =
JSRootGeometry(this, meta).apply(action).also { addChild(it) } JSRootGeometry(this, meta).apply(action).also { add(it) }
//fun Meta.toDynamic(): dynamic { //fun Meta.toDynamic(): dynamic {
// fun MetaItem<*>.toDynamic(): dynamic = when (this) { // fun MetaItem<*>.toDynamic(): dynamic = when (this) {

View File

@ -3,9 +3,9 @@ package hep.dataforge.vis.spatial.jsroot
import hep.dataforge.meta.EmptyMeta import hep.dataforge.meta.EmptyMeta
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.meta.toDynamic import hep.dataforge.meta.toDynamic
import hep.dataforge.vis.common.DisplayGroup
import hep.dataforge.vis.common.DisplayLeaf import hep.dataforge.vis.common.DisplayLeaf
import hep.dataforge.vis.common.DisplayObject import hep.dataforge.vis.common.DisplayObject
import hep.dataforge.vis.common.DisplayObjectList
import hep.dataforge.vis.common.node import hep.dataforge.vis.common.node
import hep.dataforge.vis.spatial.ThreeFactory import hep.dataforge.vis.spatial.ThreeFactory
import info.laht.threekt.core.Object3D import info.laht.threekt.core.Object3D
@ -28,7 +28,7 @@ object ThreeJSRootObjectFactory : ThreeFactory<JSRootObject> {
} }
} }
fun DisplayObjectList.jsRootObject(str: String) { fun DisplayGroup.jsRootObject(str: String) {
val json = JSON.parse<Any>(str) val json = JSON.parse<Any>(str)
JSRootObject(this, EmptyMeta, json).also { addChild(it) } JSRootObject(this, EmptyMeta, json).also { add(it) }
} }

View File

@ -4,18 +4,6 @@
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Three js demo for particle physics</title> <title>Three js demo for particle physics</title>
<!-- <style>-->
<!-- body {-->
<!-- margin: 0;-->
<!-- overflow: hidden;-->
<!-- }-->
<!-- </style>-->
<!--<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/three.js/88/three.min.js"></script>-->
<!--<script type="text/javascript" src="js/OrbitControls.js"></script>-->
<!--<script type="text/javascript" src="js/three.min.js"></script>-->
<!--<script type="text/javascript" src="js/ThreeCSG.js"></script>-->
<!--<script type="text/javascript" src="js/JSRootCore.js"></script>-->
<!--<script type="text/javascript" src="js/JSRootGeoBase.js"></script>-->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"> integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<script type="text/javascript" src="main.bundle.js"></script> <script type="text/javascript" src="main.bundle.js"></script>
@ -34,9 +22,9 @@
</div> </div>
<div class="container" id="canvas"></div> <div class="container" id="canvas"></div>
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" <!--<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"-->
integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" <!-- integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"-->
crossorigin="anonymous"></script> <!-- crossorigin="anonymous"></script>-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"
integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1"
crossorigin="anonymous"></script> crossorigin="anonymous"></script>

View File

@ -2,9 +2,9 @@ package hep.dataforge.vis.spatial
import hep.dataforge.meta.EmptyMeta import hep.dataforge.meta.EmptyMeta
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.vis.common.DisplayGroup
import hep.dataforge.vis.common.DisplayLeaf import hep.dataforge.vis.common.DisplayLeaf
import hep.dataforge.vis.common.DisplayObject import hep.dataforge.vis.common.DisplayObject
import hep.dataforge.vis.common.DisplayObjectList
import hep.dataforge.vis.common.double import hep.dataforge.vis.common.double
class Box(parent: DisplayObject?, meta: Meta) : DisplayLeaf(parent, meta), Shape { 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 //TODO add helper for color configuration
override fun <T : Any> GeometryBuilder<T>.buildGeometry() { override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) {
val dx = xSize / 2 val dx = xSize / 2
val dy = ySize / 2 val dy = ySize / 2
val dz = zSize / 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 node6 = Point3D(dx, -dy, dz)
val node7 = Point3D(dx, dy, dz) val node7 = Point3D(dx, dy, dz)
val node8 = Point3D(-dx, dy, dz) val node8 = Point3D(-dx, dy, dz)
face4(node1, node4, node3, node2, Point3D(0, 0, -1)) geometryBuilder.face4(node1, node4, node3, node2, Point3D(0, 0, -1))
face4(node1, node2, node6, node5, Point3D(0, -1, 0)) geometryBuilder.face4(node1, node2, node6, node5, Point3D(0, -1, 0))
face4(node2, node3, node7, node6, Point3D(1, 0, 0)) geometryBuilder.face4(node2, node3, node7, node6, Point3D(1, 0, 0))
face4(node4, node8, node7, node3, Point3D(0, 1, 0)) geometryBuilder.face4(node4, node8, node7, node3, Point3D(0, 1, 0))
face4(node1, node5, node8, node4, Point3D(-1, 0, 0)) geometryBuilder.face4(node1, node5, node8, node4, Point3D(-1, 0, 0))
face4(node8, node5, node6, node7, Point3D(0, 0, 1)) geometryBuilder.face4(node8, node5, node6, node7, Point3D(0, 0, 1))
} }
companion object { 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 = {}) = fun DisplayGroup.box(meta: Meta = EmptyMeta, action: Box.() -> Unit = {}) =
Box(this, meta).apply(action).also { addChild(it) } Box(this, meta).apply(action).also { add(it) }

View File

@ -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)

View File

@ -1,10 +1,9 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.meta.* 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.DisplayLeaf
import hep.dataforge.vis.common.DisplayObject import hep.dataforge.vis.common.DisplayObject
import hep.dataforge.vis.common.DisplayObjectList
class Convex(parent: DisplayObject?, meta: Meta) : DisplayLeaf(parent, meta) { 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" const val TYPE = "geometry.3d.convex"
fun points(item: MetaItem<*>): List<Point3D> { fun points(item: MetaItem<*>): List<Point3D> {
return item.node?.getAll("point".toName())?.map { (_, value) -> return item.node?.getAll("point")?.map { (_, value) ->
Point3D(value.node["x"].number ?: 0, value.node["y"].number ?: 0, value.node["y"].number ?: 0) Point3D.from(value.node?: error("Point definition is not a node"))
} ?: emptyList() } ?: emptyList()
} }
} }
} }
fun DisplayObjectList.convex(meta: Meta = EmptyMeta, action: ConvexBuilder.() -> Unit = {}) = fun DisplayGroup.convex(meta: Meta = EmptyMeta, action: ConvexBuilder.() -> Unit = {}) =
ConvexBuilder().apply(action).build(this, meta).also { addChild(it) } ConvexBuilder().apply(action).build(this, meta).also { add(it) }
class ConvexBuilder { class ConvexBuilder {
private val points = ArrayList<Point3D>() private val points = ArrayList<Point3D>()

View File

@ -1,9 +1,9 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.meta.* import hep.dataforge.meta.*
import hep.dataforge.vis.common.DisplayGroup
import hep.dataforge.vis.common.DisplayLeaf import hep.dataforge.vis.common.DisplayLeaf
import hep.dataforge.vis.common.DisplayObject import hep.dataforge.vis.common.DisplayObject
import hep.dataforge.vis.common.DisplayObjectList
import kotlin.math.PI import kotlin.math.PI
import kotlin.math.cos import kotlin.math.cos
import kotlin.math.sin import kotlin.math.sin
@ -46,7 +46,7 @@ class Extruded(parent: DisplayObject?, meta: Meta) : DisplayLeaf(parent, meta),
val shape val shape
get() = properties.getAll("shape.point").map { (_, value) -> 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) { fun shape(block: Shape2DBuilder.() -> Unit) {
@ -70,7 +70,7 @@ class Extruded(parent: DisplayObject?, meta: Meta) : DisplayLeaf(parent, meta),
return layer return layer
} }
override fun <T : Any> GeometryBuilder<T>.buildGeometry() { override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) {
val shape: Shape2D = shape val shape: Shape2D = shape
if (shape.size < 3) error("Extruded shape requires more than points per layer") 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] upperLayer = layers[i]
for (j in (0 until shape.size - 1)) { for (j in (0 until shape.size - 1)) {
//counter clockwise //counter clockwise
face4( geometryBuilder.face4(
lowerLayer[j], lowerLayer[j],
lowerLayer[j + 1], lowerLayer[j + 1],
upperLayer[j + 1], upperLayer[j + 1],
@ -104,7 +104,7 @@ class Extruded(parent: DisplayObject?, meta: Meta) : DisplayLeaf(parent, meta),
} }
// final face // final face
face4( geometryBuilder.face4(
lowerLayer[shape.size - 1], lowerLayer[shape.size - 1],
lowerLayer[0], lowerLayer[0],
upperLayer[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 = {}) = fun DisplayGroup.extrude(meta: Meta = EmptyMeta, action: Extruded.() -> Unit = {}) =
Extruded(this, meta).apply(action).also { addChild(it) } Extruded(this, meta).apply(action).also { add(it) }

View File

@ -1,16 +1,19 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.meta.EmptyMeta import hep.dataforge.meta.*
import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaRepr
import hep.dataforge.meta.buildMeta
import hep.dataforge.vis.common.DisplayObject 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 { override fun toMeta(): Meta = buildMeta {
"x" to x "x" to x
"y" to y "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 { 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 "y" to y
"z" to z "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 * @param T the type of resulting geometry
*/ */
interface GeometryBuilder<T: Any> { interface GeometryBuilder<T : Any> {
/** /**
* Add a face to 3D model. If one of the vertices is not present in the current geometry model list of vertices, * 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. * it is added automatically.
@ -35,7 +44,6 @@ interface GeometryBuilder<T: Any> {
fun face(vertex1: Point3D, vertex2: Point3D, vertex3: Point3D, normal: Point3D? = null, meta: Meta = EmptyMeta) fun face(vertex1: Point3D, vertex2: Point3D, vertex3: Point3D, normal: Point3D? = null, meta: Meta = EmptyMeta)
fun build(): T fun build(): T
} }
fun GeometryBuilder<*>.face4( fun GeometryBuilder<*>.face4(
@ -50,6 +58,6 @@ fun GeometryBuilder<*>.face4(
face(vertex1, vertex3, vertex4, normal, meta) face(vertex1, vertex3, vertex4, normal, meta)
} }
interface Shape: DisplayObject { interface Shape : DisplayObject {
fun <T: Any> GeometryBuilder<T>.buildGeometry() fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>)
} }

View File

@ -4,15 +4,14 @@ import hep.dataforge.meta.*
import hep.dataforge.output.Output import hep.dataforge.output.Output
import hep.dataforge.vis.common.DisplayGroup import hep.dataforge.vis.common.DisplayGroup
import hep.dataforge.vis.common.DisplayObject import hep.dataforge.vis.common.DisplayObject
import hep.dataforge.vis.common.DisplayObjectList
import hep.dataforge.vis.common.getProperty import hep.dataforge.vis.common.getProperty
fun DisplayObjectList.group(meta: Meta = EmptyMeta, action: DisplayObjectList.() -> Unit = {}): DisplayGroup = fun DisplayGroup.group(meta: Meta = EmptyMeta, action: DisplayGroup.() -> Unit = {}): DisplayGroup =
DisplayObjectList(this, meta).apply(action).also { addChild(it) } DisplayGroup(this, meta).apply(action).also { add(it) }
fun Output<DisplayObject>.render(meta: Meta = EmptyMeta, action: DisplayObjectList.() -> Unit) = fun Output<DisplayObject>.render(meta: Meta = EmptyMeta, action: DisplayGroup.() -> Unit) =
render(DisplayObjectList(null, EmptyMeta).apply(action), meta) render(DisplayGroup(null, EmptyMeta).apply(action), meta)
//TODO replace properties by containers? //TODO replace properties by containers?
@ -133,11 +132,11 @@ var DisplayObject.scaleZ
} }
fun DisplayObject.color(rgb: Int) { fun DisplayObject.color(rgb: Int) {
this.properties["color"] = rgb this.properties["material"] = rgb
} }
fun DisplayObject.color(meta: Meta) { fun DisplayObject.color(meta: Meta) {
this.properties["color"] = meta this.properties["material"] = meta
} }
fun DisplayObject.color(r: Int, g: Int, b: Int) = color(buildMeta { fun DisplayObject.color(r: Int, g: Int, b: Int) = color(buildMeta {

View File

@ -4,14 +4,14 @@ import hep.dataforge.meta.get
import hep.dataforge.meta.getAll import hep.dataforge.meta.getAll
import hep.dataforge.meta.node import hep.dataforge.meta.node
import hep.dataforge.names.toName import hep.dataforge.names.toName
import hep.dataforge.vis.common.DisplayObjectList import hep.dataforge.vis.common.DisplayGroup
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
class ConvexTest { class ConvexTest {
@Test @Test
fun testConvexBuilder() { fun testConvexBuilder() {
val group = DisplayObjectList().apply { val group = DisplayGroup().apply {
convex { convex {
point(50, 50, -50) point(50, 50, -50)
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 val pointsNode = convex.properties["points"].node