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