Moved visualization to a separate project

This commit is contained in:
Alexander Nozik 2019-03-08 11:20:08 +03:00
parent f8266d35c2
commit e5e399e041
29 changed files with 16 additions and 1271 deletions

1
.gitignore vendored
View File

@ -7,3 +7,4 @@
!gradle-wrapper.jar
gradle.properties

View File

@ -7,6 +7,7 @@ import hep.dataforge.names.Name
import hep.dataforge.names.NameToken
import hep.dataforge.names.plus
import hep.dataforge.names.toName
import hep.dataforge.values.EnumValue
import hep.dataforge.values.Value
import hep.dataforge.values.boolean
@ -57,7 +58,8 @@ interface Meta : MetaRepr {
*/
operator fun <T> Map<NameToken, T>.get(body: String, query: String = ""): T? = get(NameToken(body, query))
operator fun Meta.get(name: Name): MetaItem<out Meta>? {
operator fun Meta?.get(name: Name): MetaItem<out Meta>? {
if (this == null) return null
return name.first()?.let { token ->
val tail = name.cutFirst()
when (tail.length) {
@ -67,8 +69,8 @@ operator fun Meta.get(name: Name): MetaItem<out Meta>? {
}
}
operator fun Meta.get(token: NameToken): MetaItem<out Meta>? = items[token]
operator fun Meta.get(key: String): MetaItem<out Meta>? = get(key.toName())
operator fun Meta?.get(token: NameToken): MetaItem<out Meta>? = this?.items?.get(token)
operator fun Meta?.get(key: String): MetaItem<out Meta>? = get(key.toName())
/**
* Get all items matching given name.
@ -168,10 +170,17 @@ val MetaItem<*>?.string get() = value?.string
val MetaItem<*>?.boolean get() = value?.boolean
val MetaItem<*>?.number get() = value?.number
val MetaItem<*>?.double get() = number?.toDouble()
val MetaItem<*>?.float get() = number?.toFloat()
val MetaItem<*>?.int get() = number?.toInt()
val MetaItem<*>?.long get() = number?.toLong()
val MetaItem<*>?.short get() = number?.toShort()
inline fun <reified E : Enum<E>> MetaItem<*>?.enum() = if (this is ValueItem && this.value is EnumValue<*>) {
this.value as E
} else {
string?.let { enumValueOf<E>(it) }
}
val MetaItem<*>?.stringList get() = value?.list?.map { it.string } ?: emptyList()
val <M : Meta> MetaItem<M>?.node: M?

View File

@ -34,7 +34,7 @@ class Styled(val base: Meta, val style: Config = Config().empty()) : MutableMeta
if (item == null) {
style.remove(name)
} else {
style.set(name, item)
style[name] = item
}
}

View File

@ -1,25 +0,0 @@
plugins {
kotlin("multiplatform")
}
kotlin {
jvm()
js()
sourceSets {
val commonMain by getting {
dependencies {
api(project(":dataforge-io"))
}
}
val jvmMain by getting {
dependencies {
//api("no.tornado:tornadofx:1.7.18")
}
}
val jsMain by getting {
dependencies {
}
}
}
}

View File

@ -1,17 +0,0 @@
import org.openjfx.gradle.JavaFXOptions
plugins {
kotlin("jvm")
id("org.openjfx.javafxplugin")
}
dependencies {
api(project(":dataforge-vis"))
api(project(":dataforge-vis:dataforge-vis-spatial"))
api("no.tornado:tornadofx:1.7.18")
implementation("org.fxyz3d:fxyz3d:0.4.0")
}
configure<JavaFXOptions> {
modules("javafx.controls")
}

View File

@ -1,166 +0,0 @@
package hep.dataforge.vis.spatial
import javafx.event.EventHandler
import javafx.scene.*
import javafx.scene.input.KeyCode
import javafx.scene.input.KeyEvent
import javafx.scene.input.MouseEvent
import javafx.scene.input.ScrollEvent
import javafx.scene.paint.Color
import org.fxyz3d.utils.CameraTransformer
import tornadofx.*
class Canvas3D : Fragment() {
val world: Group = Group()
private val camera = PerspectiveCamera().apply {
nearClip = CAMERA_NEAR_CLIP
farClip = CAMERA_FAR_CLIP
translateZ = CAMERA_INITIAL_DISTANCE
}
private val cameraShift = CameraTransformer().apply {
val cameraFlip = CameraTransformer()
cameraFlip.children.add(camera)
cameraFlip.setRotateZ(180.0)
children.add(cameraFlip)
}
val translationXProperty get() = cameraShift.t.xProperty()
var translateX by translationXProperty
val translationYProperty get() = cameraShift.t.yProperty()
var translateY by translationYProperty
val translationZProperty get() = cameraShift.t.zProperty()
var translateZ by translationZProperty
private val cameraRotation = CameraTransformer().apply {
children.add(cameraShift)
ry.angle = CAMERA_INITIAL_Y_ANGLE
rx.angle = CAMERA_INITIAL_X_ANGLE
rz.angle = CAMERA_INITIAL_Z_ANGLE
}
val rotationXProperty get() = cameraRotation.rx.angleProperty()
var angleX by rotationXProperty
val rotationYProperty get() = cameraRotation.ry.angleProperty()
var angleY by rotationYProperty
val rotationZProperty get() = cameraRotation.rz.angleProperty()
var angleZ by rotationZProperty
override val root =borderpane {
center = SubScene(
Group(world, cameraRotation).apply { DepthTest.ENABLE },
1024.0,
768.0,
true,
SceneAntialiasing.BALANCED
).apply {
fill = Color.GREY
this.camera = this@Canvas3D.camera
id = "canvas"
handleKeyboard(this)
handleMouse(this)
}
}
private fun handleKeyboard(scene: SubScene) {
scene.onKeyPressed = EventHandler<KeyEvent> { event ->
if (event.isControlDown) {
when (event.code) {
KeyCode.Z -> {
cameraShift.t.x = 0.0
cameraShift.t.y = 0.0
camera.translateZ = CAMERA_INITIAL_DISTANCE
cameraRotation.ry.angle = CAMERA_INITIAL_Y_ANGLE
cameraRotation.rx.angle = CAMERA_INITIAL_X_ANGLE
}
// KeyCode.X -> axisGroup.isVisible = !axisGroup.isVisible
// KeyCode.S -> snapshot()
// KeyCode.DIGIT1 -> pixelMap.filterKeys { it.getLayerNumber() == 1 }.values.forEach {
// toggleTransparency(
// it
// )
// }
// KeyCode.DIGIT2 -> pixelMap.filterKeys { it.getLayerNumber() == 2 }.values.forEach {
// toggleTransparency(
// it
// )
// }
// KeyCode.DIGIT3 -> pixelMap.filterKeys { it.getLayerNumber() == 3 }.values.forEach {
// toggleTransparency(
// it
// )
// }
else -> {
}//do nothing
}
}
}
}
private fun handleMouse(scene: SubScene) {
var mousePosX: Double = 0.0
var mousePosY: Double = 0.0
var mouseOldX: Double = 0.0
var mouseOldY: Double = 0.0
var mouseDeltaX: Double = 0.0
var mouseDeltaY: Double = 0.0
scene.onMousePressed = EventHandler<MouseEvent> { me ->
mousePosX = me.sceneX
mousePosY = me.sceneY
mouseOldX = me.sceneX
mouseOldY = me.sceneY
}
scene.onMouseDragged = EventHandler<MouseEvent> { me ->
mouseOldX = mousePosX
mouseOldY = mousePosY
mousePosX = me.sceneX
mousePosY = me.sceneY
mouseDeltaX = mousePosX - mouseOldX
mouseDeltaY = mousePosY - mouseOldY
val modifier = when {
me.isControlDown -> CONTROL_MULTIPLIER
me.isShiftDown -> SHIFT_MULTIPLIER
else -> 1.0
}
if (me.isPrimaryButtonDown) {
cameraRotation.rz.angle =
cameraRotation.rz.angle + mouseDeltaX * MOUSE_SPEED * modifier * ROTATION_SPEED
cameraRotation.rx.angle =
cameraRotation.rx.angle + mouseDeltaY * MOUSE_SPEED * modifier * ROTATION_SPEED
} else if (me.isSecondaryButtonDown) {
cameraShift.t.x = cameraShift.t.x + mouseDeltaX * MOUSE_SPEED * modifier * TRACK_SPEED
cameraShift.t.y = cameraShift.t.y + mouseDeltaY * MOUSE_SPEED * modifier * TRACK_SPEED
}
}
scene.onScroll = EventHandler<ScrollEvent> { event ->
val z = camera.translateZ
val newZ = z + MOUSE_SPEED * event.deltaY * RESIZE_SPEED
camera.translateZ = newZ
}
}
companion object {
private const val CAMERA_INITIAL_DISTANCE = -4500.0
private const val CAMERA_INITIAL_X_ANGLE = -50.0
private const val CAMERA_INITIAL_Y_ANGLE = 0.0
private const val CAMERA_INITIAL_Z_ANGLE = -210.0
private const val CAMERA_NEAR_CLIP = 0.1
private const val CAMERA_FAR_CLIP = 10000.0
private const val AXIS_LENGTH = 2000.0
private const val CONTROL_MULTIPLIER = 0.1
private const val SHIFT_MULTIPLIER = 10.0
private const val MOUSE_SPEED = 0.1
private const val ROTATION_SPEED = 2.0
private const val TRACK_SPEED = 6.0
private const val RESIZE_SPEED = 50.0
private const val LINE_WIDTH = 3.0
}
}

View File

@ -1,60 +0,0 @@
package hep.dataforge.vis.spatial
import hep.dataforge.context.Context
import hep.dataforge.io.Output
import hep.dataforge.meta.Meta
import hep.dataforge.vis.DisplayGroup
import hep.dataforge.vis.DisplayObjectPropertyListener
import hep.dataforge.vis.float
import hep.dataforge.vis.transform
import javafx.scene.Group
import javafx.scene.Node
import org.fxyz3d.shapes.primitives.CuboidMesh
import tornadofx.*
/**
* https://github.com/miho/JCSG for operations
*
*/
class FX3DOutput(override val context: Context) : Output<Any> {
val canvas by lazy { Canvas3D() }
private fun buildNode(obj: Any): Node? {
return when (obj) {
is DisplayShape3D -> {
val listener = DisplayObjectPropertyListener(obj)
val x = listener["x"].float()
val y = listener["y"].float()
val z = listener["z"].float()
val center = objectBinding(x, y, z) {
org.fxyz3d.geometry.Point3D(x.value ?: 0f, y.value ?: 0f, z.value ?: 0f)
}
when (obj) {
is DisplayGroup3D -> Group(obj.children.map { buildNode(it) }).apply {
this.translateXProperty().bind(x)
this.translateYProperty().bind(y)
this.translateZProperty().bind(z)
}
is Box -> CuboidMesh(obj.xSize, obj.ySize, obj.zSize).apply {
this.centerProperty().bind(center)
this.materialProperty().bind(listener["color"].transform { it.material() })
}
else -> {
logger.error { "No renderer defined for ${obj::class}" }
null
}
}
}
is DisplayGroup -> Group(obj.children.map { buildNode(it) }) // a logical group
else -> {
logger.error { "No renderer defined for ${obj::class}" }
null
}
}
}
override fun render(obj: Any, meta: Meta) {
buildNode(obj)?.let { canvas.world.children.add(it) }
}
}

View File

@ -1,69 +0,0 @@
package hep.dataforge.vis.spatial
import hep.dataforge.meta.MetaItem
import hep.dataforge.meta.double
import hep.dataforge.meta.get
import hep.dataforge.meta.int
import hep.dataforge.values.ValueType
import javafx.scene.paint.Color
import javafx.scene.paint.Material
import javafx.scene.paint.PhongMaterial
object Materials {
val RED = PhongMaterial().apply {
diffuseColor = Color.DARKRED
specularColor = Color.RED
}
val WHITE = PhongMaterial().apply {
diffuseColor = Color.WHITE
specularColor = Color.LIGHTBLUE
}
val GREY = PhongMaterial().apply {
diffuseColor = Color.DARKGREY
specularColor = Color.GREY
}
val BLUE = PhongMaterial(Color.BLUE)
}
/**
* Infer color based on meta item
*/
fun MetaItem<*>.color(): Color {
return when (this) {
is MetaItem.ValueItem -> if (this.value.type == ValueType.STRING) {
Color.web(this.value.string)
} else {
val int = value.number.toInt()
val red = int and 0x00ff0000 shr 16
val green = int and 0x0000ff00 shr 8
val blue = int and 0x000000ff
Color.rgb(red, green, blue)
}
is MetaItem.NodeItem -> {
Color.rgb(
node["red"]?.int ?: 0,
node["green"]?.int ?: 0,
node["blue"]?.int ?: 0,
node["opacity"]?.double ?: 1.0
)
}
}
}
/**
* Infer FX material based on meta item
*/
fun MetaItem<*>?.material(): Material {
return when (this) {
null -> Materials.GREY
is MetaItem.ValueItem -> PhongMaterial(color())
is MetaItem.NodeItem -> PhongMaterial().apply {
(node["color"]?: this@material).let { diffuseColor = it.color() }
node["specularColor"]?.let { specularColor = it.color() }
}
}
}

View File

@ -1,64 +0,0 @@
package hep.dataforge.vis.spatial
import hep.dataforge.context.Global
import hep.dataforge.meta.number
import hep.dataforge.vis.DisplayGroup
import javafx.scene.Parent
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import tornadofx.*
import kotlin.random.Random
class RendererDemoApp : App(RendererDemoView::class)
class RendererDemoView : View() {
val renderer = FX3DOutput(Global)
override val root: Parent = borderpane {
center = renderer.canvas.root
}
lateinit var group: DisplayGroup
init {
renderer.render {
group = group {
box {
xSize = 100.0
ySize = 100.0
zSize = 100.0
}
box {
x = 110.0
xSize = 100.0
ySize = 100.0
zSize = 100.0
}
}
}
var color by group.properties.number(1530).int
GlobalScope.launch {
val random = Random(111)
while (isActive) {
delay(1000)
color = random.nextInt(0, Int.MAX_VALUE)
}
}
renderer.canvas.apply {
angleY = -30.0
angleX = -15.0
}
}
}
fun main() {
launch<RendererDemoApp>()
}

View File

@ -1,59 +0,0 @@
import org.jetbrains.kotlin.gradle.frontend.KotlinFrontendExtension
import org.jetbrains.kotlin.gradle.frontend.npm.NpmExtension
import org.jetbrains.kotlin.gradle.frontend.webpack.WebPackExtension
plugins {
id("kotlin2js")
id("kotlin-dce-js")
id("org.jetbrains.kotlin.frontend")
}
dependencies {
api(project(":dataforge-vis:dataforge-vis-spatial"))
implementation("info.laht.threekt:threejs-wrapper:0.88-npm-1")
}
configure<KotlinFrontendExtension> {
downloadNodeJsVersion = "latest"
configure<NpmExtension> {
dependency("three")
dependency("three-orbitcontrols")
dependency("style-loader")
devDependency("karma")
}
sourceMaps = true
bundle("webpack") {
this as WebPackExtension
bundleName = "main"
proxyUrl = "http://localhost:8080"
contentPath = file("src/main/web")
sourceMapEnabled = true
//mode = "production"
mode = "development"
}
}
tasks{
compileKotlin2Js{
kotlinOptions{
metaInfo = true
outputFile = "${project.buildDir.path}/js/${project.name}.js"
sourceMap = true
moduleKind = "umd"
main = "call"
}
}
compileTestKotlin2Js{
kotlinOptions{
metaInfo = true
outputFile = "${project.buildDir.path}/js/${project.name}-test.js"
sourceMap = true
moduleKind = "umd"
}
}
}

View File

@ -1,3 +0,0 @@
{
"description": "A demo project for particle visualization in JS"
}

View File

@ -1,19 +0,0 @@
package hep.dataforge.vis.hmr
external val module: Module
external interface Module {
val hot: Hot?
}
external interface Hot {
val data: dynamic
fun accept()
fun accept(dependency: String, callback: () -> Unit)
fun accept(dependencies: Array<String>, callback: (updated: Array<String>) -> Unit)
fun dispose(callback: (data: dynamic) -> Unit)
}
external fun require(name: String): dynamic

View File

@ -1,50 +0,0 @@
package hep.dataforge.vis
import hep.dataforge.vis.hmr.module
import hep.dataforge.vis.spatial.ThreeDemoApp
import kotlin.browser.document
import kotlin.dom.hasClass
abstract class ApplicationBase {
abstract val stateKeys: List<String>
abstract fun start(state: Map<String, Any>)
abstract fun dispose(): Map<String, Any>
}
fun main() {
var application: ApplicationBase? = null
val state: dynamic = module.hot?.let { hot ->
hot.accept()
hot.dispose { data ->
data.appState = application?.dispose()
application = null
}
hot.data
}
if (document.body != null) {
application = start(state)
} else {
application = null
document.addEventListener("DOMContentLoaded", { application = start(state) })
}
}
fun start(state: dynamic): ApplicationBase? {
return if (document.body?.hasClass("testApp") == true) {
val application = ThreeDemoApp()
@Suppress("UnsafeCastFromDynamic")
application.start(state?.appState ?: emptyMap())
application
} else {
null
}
}

View File

@ -1,59 +0,0 @@
package hep.dataforge.vis.spatial
import hep.dataforge.meta.MetaItem
import hep.dataforge.meta.double
import hep.dataforge.meta.get
import hep.dataforge.meta.int
import hep.dataforge.values.ValueType
import info.laht.threekt.materials.Material
import info.laht.threekt.materials.MeshPhongMaterial
import info.laht.threekt.math.Color
import info.laht.threekt.math.ColorConstants
object Materials {
val DEFAULT = MeshPhongMaterial().apply {
this.color.set(ColorConstants.darkgreen)
}
}
/**
* Infer color based on meta item
*/
fun MetaItem<*>.color(): Color {
return when (this) {
is MetaItem.ValueItem -> if (this.value.type == ValueType.STRING) {
Color(this.value.string)
} else {
val int = value.number.toInt()
val red = int and 0x00ff0000 shr 16
val green = int and 0x0000ff00 shr 8
val blue = int and 0x000000ff
Color(red, green, blue)
}
is MetaItem.NodeItem -> {
Color(
node["red"]?.int ?: 0,
node["green"]?.int ?: 0,
node["blue"]?.int ?: 0
)
}
}
}
/**
* Infer FX material based on meta item
*/
fun MetaItem<*>?.material(): Material {
return when (this) {
null -> Materials.DEFAULT
is MetaItem.ValueItem -> MeshPhongMaterial().apply {
color = this@material.color()
}
is MetaItem.NodeItem -> MeshPhongMaterial().apply {
(node["color"] ?: this@material).let { color = it.color() }
opacity = node["opacity"]?.double ?: 1.0
node["specularColor"]?.let { specular = it.color() }
}
}
}

View File

@ -1,65 +0,0 @@
package hep.dataforge.vis.spatial
import hep.dataforge.context.Global
import hep.dataforge.meta.number
import hep.dataforge.vis.ApplicationBase
import hep.dataforge.vis.DisplayGroup
import info.laht.threekt.external.controls.OrbitControls
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlin.browser.document
import kotlin.random.Random
class ThreeDemoApp : ApplicationBase() {
override val stateKeys: List<String> = emptyList()
override fun start(state: Map<String, Any>) {
val renderer = ThreeOutput(Global)
renderer.start(document.getElementById("canvas")!!)
println("started")
lateinit var group: DisplayGroup
renderer.render {
group = group {
box {
xSize = 100.0
ySize = 100.0
zSize = 100.0
}
box {
x = 110.0
xSize = 100.0
ySize = 100.0
zSize = 100.0
}
}
}
var color by group.properties.number(1530).int
GlobalScope.launch {
val random = Random(111)
while (isActive) {
delay(1000)
color = random.nextInt(0, Int.MAX_VALUE)
}
}
// view.animate()
// view = WebLinesView(document.getElementById("lines")!!, document.getElementById("addForm")!!)
// presenter = LinesPresenter(view)
//
// state["lines"]?.let { linesState ->
// @Suppress("UNCHECKED_CAST")
// presenter.restore(linesState as Array<String>)
// }
}
override fun dispose() = emptyMap<String,Any>()//mapOf("lines" to presenter.dispose())
}

View File

@ -1,147 +0,0 @@
package hep.dataforge.vis.spatial
import hep.dataforge.context.Context
import hep.dataforge.io.Output
import hep.dataforge.meta.Meta
import hep.dataforge.meta.get
import hep.dataforge.vis.DisplayGroup
import info.laht.threekt.WebGLRenderer
import info.laht.threekt.cameras.PerspectiveCamera
import info.laht.threekt.core.BufferGeometry
import info.laht.threekt.core.Object3D
import info.laht.threekt.external.controls.OrbitControls
import info.laht.threekt.extras.curves.CatmullRomCurve3
import info.laht.threekt.geometries.BoxBufferGeometry
import info.laht.threekt.lights.AmbientLight
import info.laht.threekt.materials.LineBasicMaterial
import info.laht.threekt.materials.MeshBasicMaterial
import info.laht.threekt.materials.MeshPhongMaterial
import info.laht.threekt.math.ColorConstants
import info.laht.threekt.math.Vector3
import info.laht.threekt.objects.Line
import info.laht.threekt.objects.Mesh
import info.laht.threekt.scenes.Scene
import org.w3c.dom.Element
import kotlin.browser.window
class ThreeOutput(override val context: Context) : Output<Any> {
private val renderer = WebGLRenderer { antialias = true }.apply {
setClearColor(ColorConstants.skyblue, 1)
setSize(window.innerWidth, window.innerHeight)
}
val scene: Scene = Scene().apply {
add(AmbientLight())
}
val camera = PerspectiveCamera(
75,
window.innerWidth.toDouble() / window.innerHeight,
0.1,
10000
).apply {
position.setZ(1000)
}
val controls: OrbitControls = OrbitControls(camera, renderer.domElement)
val root get() = renderer.domElement
private fun animate() {
window.requestAnimationFrame {
animate()
}
renderer.render(scene, camera)
}
fun start(element: Element) {
window.addEventListener("resize", {
camera.aspect = window.innerWidth.toDouble() / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight)
}, false)
element.appendChild(root)
animate()
}
private fun buildNode(obj: Any): Object3D? {
return when (obj) {
is DisplayShape3D -> {
// val listener = DisplayObjectPropertyListener(obj)
// val x = listener["x"].float()
// val y = listener["y"].float()
// val z = listener["z"].float()
// val center = objectBinding(x, y, z) {
// Vector3(x.value ?: 0f, y.value ?: 0f, z.value ?: 0f)
// }
when (obj) {
is DisplayGroup3D -> Group(obj.children.mapNotNull { buildNode(it) }).apply {
this.translateX(obj.x)
this.translateY(obj.y)
this.translateZ(obj.z)
}
is Box -> {
//TODO add bindings
val geometry = BoxBufferGeometry(obj.xSize, obj.ySize, obj.zSize)
.translate(obj.x, obj.y, obj.z)
Mesh(geometry, obj.properties["color"].material())
}
else -> {
logger.error { "No renderer defined for ${obj::class}" }
null
}
}
}
is DisplayGroup -> Group(obj.children.mapNotNull { buildNode(it) }) // a logical group
else -> {
logger.error { "No renderer defined for ${obj::class}" }
null
}
}
}
override fun render(obj: Any, meta: Meta) {
buildNode(obj)?.let { scene.add(it) }
}
// init {
// val cube: Mesh
//
// cube = Mesh(
// BoxBufferGeometry(1, 1, 1),
// MeshPhongMaterial().apply {
// this.color.set(ColorConstants.darkgreen)
// }
// ).also(scene::add)
//
// Mesh(cube.geometry as BufferGeometry,
// MeshBasicMaterial().apply {
// this.wireframe = true
// this.color.set(ColorConstants.black)
// }
// ).also(cube::add)
//
// val points = CatmullRomCurve3(
// arrayOf(
// Vector3(-10, 0, 10),
// Vector3(-5, 5, 5),
// Vector3(0, 0, 0),
// Vector3(5, -5, 5),
// Vector3(10, 0, 10)
// )
// ).getPoints(50)
//
// val geometry = BufferGeometry().setFromPoints(points)
//
// val material = LineBasicMaterial().apply {
// color.set(0xff0000)
// }
//
// // Create the final object to add to the scene
// Line(geometry, material).apply(scene::add)
// }
}

View File

@ -1,13 +0,0 @@
package hep.dataforge.vis.spatial
import info.laht.threekt.core.Object3D
/**
* Utility methods for three.kt.
* TODO move to three project
*/
@Suppress("FunctionName")
fun Group(children: Collection<Object3D>) = info.laht.threekt.objects.Group().apply {
children.forEach { this.add(it) }
}

View File

@ -1,19 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Three js demo for particle physics</title>
<style>
body {
margin: 0px;
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" language="JavaScript" src="main.bundle.js"></script>
</head>
<body class="testApp">
<h1>Demo canvas</h1>
<div id="canvas"></div>
</body>
</html>

View File

@ -1 +0,0 @@
config.module.rules.push({ test: /\.css$/, loader: "style!css" });

View File

@ -1,27 +0,0 @@
plugins {
kotlin("multiplatform")
}
kotlin {
jvm()
js()
sourceSets {
val commonMain by getting {
dependencies {
api(project(":dataforge-vis"))
}
}
val jvmMain by getting {
dependencies {
}
}
val jsMain by getting {
dependencies {
}
}
}
}

View File

@ -1,20 +0,0 @@
package hep.dataforge.vis.spatial
import hep.dataforge.meta.EmptyMeta
import hep.dataforge.meta.Meta
import hep.dataforge.vis.DisplayGroup
import hep.dataforge.vis.DisplayObject
import hep.dataforge.vis.double
class Box(parent: DisplayObject?, meta: Meta) : DisplayShape3D(parent, TYPE, meta) {
var xSize by double(1.0)
var ySize by double(1.0)
var zSize by double(1.0)
companion object {
const val TYPE = "geometry.spatial.box"
}
}
fun DisplayGroup.box(meta: Meta = EmptyMeta, action: Box.() -> Unit = {}) =
Box(this, meta).apply(action).also { addChild(it) }

View File

@ -1,39 +0,0 @@
package hep.dataforge.vis.spatial
import hep.dataforge.io.Output
import hep.dataforge.meta.EmptyMeta
import hep.dataforge.meta.Meta
import hep.dataforge.vis.*
import hep.dataforge.vis.DisplayObject.Companion.DEFAULT_TYPE
interface DisplayObject3D : DisplayObject {
val x: Double
val y: Double
val z: Double
companion object {
const val TYPE = "geometry.spatial"
}
}
open class DisplayShape3D(parent: DisplayObject?, type: String, meta: Meta) :
DisplayLeaf(parent, type, meta), DisplayObject3D {
override var x by double(0.0, inherited = false)
override var y by double(0.0, inherited = false)
override var z by double(0.0, inherited = false)
}
class DisplayGroup3D(parent: DisplayObject?, type: String, meta: Meta) : DisplayNode(parent, type, meta),
DisplayObject3D {
override var x by double(0.0, inherited = false)
override var y by double(0.0, inherited = false)
override var z by double(0.0, inherited = false)
}
fun DisplayGroup.group(meta: Meta = EmptyMeta, action: DisplayGroup.() -> Unit = {}) =
DisplayNode(this, DEFAULT_TYPE, meta).apply(action).also { addChild(it) }
fun Output<DisplayObject>.render(meta: Meta = EmptyMeta, action: DisplayGroup.() -> Unit) =
render(DisplayNode(null, DEFAULT_TYPE, EmptyMeta).apply(action), meta)

View File

@ -1,17 +0,0 @@
package hep.dataforge.vis.spatial
import hep.dataforge.meta.EmptyMeta
import hep.dataforge.meta.Meta
import hep.dataforge.vis.DisplayGroup
import hep.dataforge.vis.DisplayObject
class Extruded(parent: DisplayObject?, meta: Meta) : DisplayShape3D(parent, TYPE, meta) {
companion object {
const val TYPE = "geometry.spatial.extruded"
}
}
fun DisplayGroup.extrude(meta: Meta = EmptyMeta, action: Extruded.() -> Unit = {}) =
Extruded(this, meta).apply(action).also { addChild(it) }

View File

@ -1,8 +0,0 @@
package hep.dataforge.vis.spatial
//TODO replace by platform optimized version
data class Point3D(
val x: Float,
val y: Float,
val z: Float
)

View File

@ -1,147 +0,0 @@
package hep.dataforge.vis
import hep.dataforge.meta.*
import hep.dataforge.names.Name
import hep.dataforge.vis.DisplayObject.Companion.DEFAULT_TYPE
import hep.dataforge.vis.DisplayObject.Companion.META_KEY
import hep.dataforge.vis.DisplayObject.Companion.TAGS_KEY
/**
* A root type for display hierarchy
*/
interface DisplayObject {
/**
* The parent object of this one. If null, this one is a root.
*/
val parent: DisplayObject?
/**
* The type of this object. Uses `.` notation. Empty type means untyped group
*/
val type: String
val properties: Styled
companion object {
const val DEFAULT_TYPE = ""
const val TYPE_KEY = "@type"
const val CHILDREN_KEY = "@children"
const val META_KEY = "@meta"
const val TAGS_KEY = "@tags"
}
}
interface DisplayGroup : DisplayObject {
val children: List<DisplayObject>
/**
* Add a child object and notify listeners
*/
fun addChild(obj: DisplayObject)
/**
* Remove a specific child and notify listeners
*/
fun removeChild(obj: DisplayObject)
/**
* Add listener for children change
* TODO add detailed information into change listener
*/
fun onChildrenChange(owner: Any? = null, action: () -> Unit)
/**
* Remove children change listener
*/
fun removeChildrenChangeListener(owner: Any? = null)
}
/**
* Get the property of this display object of parent's if not found
*/
tailrec fun DisplayObject.getProperty(name: Name): MetaItem<*>? = properties[name] ?: parent?.getProperty(name)
/**
* A change listener for [DisplayObject] configuration.
*/
fun DisplayObject.onChange(owner: Any?, action: (Name, before: MetaItem<*>?, after: MetaItem<*>?) -> Unit) {
properties.style.onChange(owner, action)
parent?.onChange(owner, action)
}
/**
* Remove all meta listeners with matching owners
*/
fun DisplayObject.removeChangeListener(owner: Any?) {
properties.style.removeListener(owner)
parent?.removeChangeListener(owner)
}
/**
* Additional meta not relevant to display
*/
val DisplayObject.meta: Meta get() = properties[META_KEY]?.node ?: EmptyMeta
val DisplayObject.tags: List<String> get() = properties[TAGS_KEY].stringList
internal data class ObjectListener(
val owner: Any?,
val action: () -> Unit
)
/**
* Basic group of display objects
*/
open class DisplayNode(
override val parent: DisplayObject? = null,
override val type: String = DEFAULT_TYPE,
meta: Meta = EmptyMeta
) : DisplayGroup {
private val _children = ArrayList<DisplayObject>()
override val children: List<DisplayObject> get() = _children
override val properties = Styled(meta)
private val listeners = HashSet<ObjectListener>()
override fun addChild(obj: DisplayObject) {
// val before = _children[name]
// if (obj == null) {
// _children.remove(name)
// } else {
// _children[name] = obj
// }
// listeners.forEach { it.action(name, before, obj) }
_children.add(obj)
listeners.forEach { it.action() }
}
override fun removeChild(obj: DisplayObject) {
if (_children.remove(obj)) {
listeners.forEach { it.action }
}
}
override fun onChildrenChange(owner: Any?, action: () -> Unit) {
listeners.add(ObjectListener(owner, action))
}
override fun removeChildrenChangeListener(owner: Any?) {
listeners.removeAll { it.owner === owner }
}
}
/**
* Basic [DisplayObject] leaf element
*/
open class DisplayLeaf(
override val parent: DisplayObject?,
override val type: String,
meta: Meta = EmptyMeta
) : DisplayObject {
final override val properties = Styled(meta)
}

View File

@ -1,120 +0,0 @@
package hep.dataforge.vis
import hep.dataforge.meta.*
import hep.dataforge.names.Name
import hep.dataforge.names.toName
import hep.dataforge.values.Value
import kotlin.jvm.JvmName
import kotlin.properties.ReadOnlyProperty
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
/**
* A delegate for display object properties
*/
class DisplayObjectDelegate(
val key: Name?,
val default: MetaItem<*>?,
val inherited: Boolean
) : ReadWriteProperty<DisplayObject, MetaItem<*>?> {
override fun getValue(thisRef: DisplayObject, property: KProperty<*>): MetaItem<*>? {
val name = key ?: property.name.toName()
return if (inherited) {
thisRef.getProperty(name)
} else {
thisRef.properties[name]
} ?: default
}
override fun setValue(thisRef: DisplayObject, property: KProperty<*>, value: MetaItem<*>?) {
val name = key ?: property.name.toName()
thisRef.properties.style[name] = value
}
}
class DisplayObjectDelegateWrapper<T>(
val key: Name?,
val default: T,
val inherited: Boolean,
val write: Config.(name: Name, value: T) -> Unit = { name, value -> set(name, value) },
val read: (MetaItem<*>?) -> T?
) : ReadWriteProperty<DisplayObject, T> {
override fun getValue(thisRef: DisplayObject, property: KProperty<*>): T {
val name = key ?: property.name.toName()
return if (inherited) {
read(thisRef.getProperty(name))
} else {
read(thisRef.properties[name])
} ?: default
}
override fun setValue(thisRef: DisplayObject, property: KProperty<*>, value: T) {
val name = key ?: property.name.toName()
thisRef.properties.style.write(name, value)
}
}
fun DisplayObject.value(default: Value? = null, key: String? = null, inherited: Boolean = true) =
DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.value }
fun DisplayObject.string(default: String? = null, key: String? = null, inherited: Boolean = true) =
DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.string }
fun DisplayObject.boolean(default: Boolean? = null, key: String? = null, inherited: Boolean = true) =
DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.boolean }
fun DisplayObject.number(default: Number? = null, key: String? = null, inherited: Boolean = true) =
DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.number }
fun DisplayObject.double(default: Double? = null, key: String? = null, inherited: Boolean = true) =
DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.double }
fun DisplayObject.int(default: Int? = null, key: String? = null, inherited: Boolean = true) =
DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.int }
fun DisplayObject.node(key: String? = null, inherited: Boolean = true) =
DisplayObjectDelegateWrapper(key?.toName(), null, inherited) { it.node }
//fun <T : Configurable> Configurable.spec(spec: Specification<T>, key: String? = null) = ChildConfigDelegate<T>(key) { spec.wrap(this) }
@JvmName("safeString")
fun DisplayObject.string(default: String, key: String? = null, inherited: Boolean = true) =
DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.string }
@JvmName("safeBoolean")
fun DisplayObject.boolean(default: Boolean, key: String? = null, inherited: Boolean = true) =
DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.boolean }
@JvmName("safeNumber")
fun DisplayObject.number(default: Number, key: String? = null, inherited: Boolean = true) =
DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.number }
@JvmName("safeDouble")
fun DisplayObject.double(default: Double, key: String? = null, inherited: Boolean = true) =
DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { it.double }
inline fun <reified E : Enum<E>> DisplayObject.enum(default: E, key: String? = null, inherited: Boolean = true) =
DisplayObjectDelegateWrapper(key?.toName(), default, inherited) { item -> item.string?.let { enumValueOf<E>(it) } }
//merge properties
fun <T> DisplayObject.merge(
key: String? = null,
transformer: (Sequence<MetaItem<*>>) -> T
): ReadOnlyProperty<DisplayObject, T> {
return object : ReadOnlyProperty<DisplayObject, T> {
override fun getValue(thisRef: DisplayObject, property: KProperty<*>): T {
val name = key?.toName() ?: property.name.toName()
val sequence = sequence<MetaItem<*>> {
var thisObj: DisplayObject? = thisRef
while (thisObj != null) {
thisObj.properties[name]?.let { yield(it) }
thisObj = thisObj.parent
}
}
return transformer(sequence)
}
}
}

View File

@ -1,40 +0,0 @@
package hep.dataforge.vis
import hep.dataforge.names.Name
import hep.dataforge.names.NameToken
interface NamedObject : DisplayObject {
val name: String
operator fun get(nameToken: NameToken): DisplayGroup?
operator fun set(nameToken: NameToken, group: DisplayGroup)
}
/**
* Recursively get a child
*/
tailrec operator fun NamedObject.get(name: Name): DisplayObject? = when (name.length) {
0 -> this
1 -> this[name[0]]
else -> name.first()?.let { this[it] as? NamedObject }?.get(name.cutFirst())
}
/**
* Set given object creating intermediate empty groups if needed
* @param name - the full name of a child
* @param objFactory - a function that creates child object from parent (to avoid mutable parent parameter)
*/
fun NamedObject.set(name: Name, objFactory: (parent: DisplayObject) -> DisplayGroup): Unit = when (name.length) {
0 -> error("Can't set object with empty name")
1 -> set(name[0], objFactory(this))
else -> (this[name.first()!!] ?: DisplayNode(this))
.run {
if (this is NamedObject) {
this.set(name.cutFirst(), objFactory)
} else {
error("Can't assign child to a leaf element $this")
}
}
}

View File

@ -1,7 +0,0 @@
# Enable official Kotlin Code Style in the IDE.
kotlin.code.style=official
artifactoryUser=darksnake
artifactoryPassword=nortlander
bintrayUser=altavir
bintrayApiKey=9dcb7a779986e1b08898980269b6d428cadda0c3

View File

@ -26,9 +26,5 @@ include(
":dataforge-data",
":dataforge-io",
":dataforge-workspace",
":dataforge-scripting",
":dataforge-vis",
":dataforge-vis:dataforge-vis-spatial",
":dataforge-vis:dataforge-vis-spatial-fx",
":dataforge-vis:dataforge-vis-spatial-js"
":dataforge-scripting"
)