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{
|
||||
kotlin("jvm") version "1.3.40" apply false
|
||||
|
@ -17,19 +17,15 @@ class DisplayGroup(
|
||||
private val namedChildren = HashMap<Name, 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 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
|
||||
override fun provideTop(target: String): Map<Name, Any> {
|
||||
return when(target){
|
||||
DisplayObject.TYPE -> namedChildren
|
||||
else -> emptyMap()
|
||||
}
|
||||
}
|
||||
|
||||
@ -55,14 +51,18 @@ class DisplayGroup(
|
||||
/**
|
||||
*
|
||||
*/
|
||||
operator fun set(key: String, child: DisplayObject?) {
|
||||
val name = key.toName()
|
||||
if (child == null) {
|
||||
namedChildren.remove(name)
|
||||
operator fun set(key: String?, child: DisplayObject?) {
|
||||
if(key == null){
|
||||
|
||||
} else {
|
||||
namedChildren[name] = child
|
||||
val name = key.toName()
|
||||
if (child == null) {
|
||||
namedChildren.remove(name)
|
||||
} else {
|
||||
namedChildren[name] = child
|
||||
}
|
||||
listeners.forEach { it.callback(name, child) }
|
||||
}
|
||||
listeners.forEach { it.callback(name, child) }
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -3,13 +3,16 @@ package hep.dataforge.vis.common
|
||||
import hep.dataforge.meta.*
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.toName
|
||||
import hep.dataforge.provider.Type
|
||||
import hep.dataforge.vis.common.DisplayObject.Companion.META_KEY
|
||||
import hep.dataforge.vis.common.DisplayObject.Companion.TAGS_KEY
|
||||
import hep.dataforge.vis.common.DisplayObject.Companion.TYPE
|
||||
|
||||
/**
|
||||
* A root type for display hierarchy
|
||||
*/
|
||||
interface DisplayObject {
|
||||
@Type(TYPE)
|
||||
interface DisplayObject : MetaRepr {
|
||||
|
||||
/**
|
||||
* The parent object of this one. If null, this one is a root.
|
||||
@ -18,8 +21,12 @@ interface DisplayObject {
|
||||
|
||||
val properties: Styled
|
||||
|
||||
override fun toMeta(): Meta = buildMeta(properties) {
|
||||
"type" to this::class
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TARGET = "display"
|
||||
const val TYPE = "display"
|
||||
|
||||
const val DEFAULT_TYPE = ""
|
||||
//const val TYPE_KEY = "@type"
|
||||
|
@ -115,5 +115,5 @@ class ApplicationSurrogate : App() {
|
||||
}
|
||||
|
||||
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 {
|
||||
implementation(project(":dataforge-vis-spatial"))
|
||||
//implementation("ch.viseon.threejs:wrapper:105.0.0")
|
||||
implementation("info.laht.threekt:threejs-wrapper:0.88-npm-2")
|
||||
implementation("info.laht.threekt:threejs-wrapper:0.106-npm-2")
|
||||
testCompile(kotlin("test-js"))
|
||||
}
|
||||
|
||||
@ -22,7 +21,7 @@ configure<KotlinFrontendExtension> {
|
||||
downloadNodeJsVersion = "latest"
|
||||
|
||||
configure<NpmExtension> {
|
||||
dependency("three-full")
|
||||
dependency("three","0.106.2")
|
||||
dependency("@hi-level/three-csg")
|
||||
dependency("style-loader")
|
||||
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.meta.number
|
||||
import hep.dataforge.vis.common.Colors
|
||||
import hep.dataforge.vis.spatial.*
|
||||
import hep.dataforge.vis.spatial.jsroot.JSRootPlugin
|
||||
import hep.dataforge.vis.spatial.jsroot.jsRootGeometry
|
||||
@ -28,7 +29,7 @@ class ThreeDemoApp : ApplicationBase() {
|
||||
}.build()
|
||||
|
||||
context.plugins.load(ThreeDemoGrid()).run {
|
||||
demo("group", "Group demo") {
|
||||
demo("dynamic", "Dynamic properties demo") {
|
||||
val group = group {
|
||||
box {
|
||||
z = 110.0
|
||||
@ -42,6 +43,7 @@ class ThreeDemoApp : ApplicationBase() {
|
||||
xSize = 100.0
|
||||
ySize = 100.0
|
||||
zSize = 100.0
|
||||
//override color for this cube
|
||||
color(1530)
|
||||
|
||||
GlobalScope.launch {
|
||||
@ -53,13 +55,13 @@ class ThreeDemoApp : ApplicationBase() {
|
||||
}
|
||||
}
|
||||
|
||||
var color by group.properties.number(1530).int
|
||||
var material by group.properties.number(1530).int
|
||||
|
||||
GlobalScope.launch {
|
||||
val random = Random(111)
|
||||
while (isActive) {
|
||||
delay(1000)
|
||||
color = random.nextInt(0, Int.MAX_VALUE)
|
||||
material = random.nextInt(0, Int.MAX_VALUE)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -68,7 +70,9 @@ class ThreeDemoApp : ApplicationBase() {
|
||||
jsRootGeometry {
|
||||
y = 110.0
|
||||
shape = box(50, 50, 50)
|
||||
color(12285)
|
||||
color(Colors.lightcoral)
|
||||
rotationX = PI / 4
|
||||
rotationY = PI / 4
|
||||
}
|
||||
}
|
||||
|
||||
@ -81,6 +85,40 @@ class ThreeDemoApp : ApplicationBase() {
|
||||
layer(i * 5, 20 * sin(2 * PI / 100 * i), 20 * cos(2 * PI / 100 * i))
|
||||
}
|
||||
}
|
||||
|
||||
color(Colors.teal)
|
||||
}
|
||||
|
||||
demo("CSG", "CSG operations") {
|
||||
composite(CompositeType.UNION) {
|
||||
box(100, 100, 100) {
|
||||
z = 100
|
||||
rotationX = PI / 4
|
||||
rotationY = PI / 4
|
||||
}
|
||||
box(100, 100, 100)
|
||||
color(Colors.green)
|
||||
}
|
||||
composite(CompositeType.INTERSECT) {
|
||||
box(100, 100, 100) {
|
||||
z = 100
|
||||
rotationX = PI / 4
|
||||
rotationY = PI / 4
|
||||
}
|
||||
box(100, 100, 100)
|
||||
y = 300
|
||||
color(Colors.red)
|
||||
}
|
||||
composite(CompositeType.SUBTRACT) {
|
||||
box(100, 100, 100) {
|
||||
z = 100
|
||||
rotationX = PI / 4
|
||||
rotationY = PI / 4
|
||||
}
|
||||
box(100, 100, 100)
|
||||
y = -300
|
||||
color(Colors.blue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,8 +14,10 @@ import hep.dataforge.output.Output
|
||||
import hep.dataforge.output.OutputManager
|
||||
import hep.dataforge.vis.common.DisplayGroup
|
||||
import hep.dataforge.vis.common.DisplayObject
|
||||
import hep.dataforge.vis.spatial.ThreeOutput
|
||||
import hep.dataforge.vis.spatial.render
|
||||
import hep.dataforge.vis.spatial.three.ThreeOutput
|
||||
import hep.dataforge.vis.spatial.three.ThreePlugin
|
||||
import hep.dataforge.vis.spatial.three.output
|
||||
import kotlinx.html.dom.append
|
||||
import kotlinx.html.dom.create
|
||||
import kotlinx.html.h2
|
||||
@ -33,6 +35,8 @@ class ThreeDemoGrid(meta: Meta) : AbstractPlugin(meta), OutputManager {
|
||||
private val gridRoot = document.create.div("row")
|
||||
private val outputs: MutableMap<Name, ThreeOutput> = HashMap()
|
||||
|
||||
override fun dependsOn(): List<PluginFactory<*>> = listOf(ThreePlugin)
|
||||
|
||||
override fun attach(context: Context) {
|
||||
super.attach(context)
|
||||
val elementId = meta["elementID"].string ?: "canvas"
|
||||
@ -42,9 +46,11 @@ class ThreeDemoGrid(meta: Meta) : AbstractPlugin(meta), OutputManager {
|
||||
}
|
||||
|
||||
override fun <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) {
|
||||
if (type != DisplayObject::class) error("Supports only DisplayObject")
|
||||
val output = ThreeOutput.build(context, meta) {
|
||||
val output = three.output(meta) {
|
||||
"axis" to {
|
||||
"size" to 500
|
||||
}
|
||||
@ -52,8 +58,8 @@ class ThreeDemoGrid(meta: Meta) : AbstractPlugin(meta), OutputManager {
|
||||
//TODO calculate cell width here using jquery
|
||||
gridRoot.append {
|
||||
span("border") {
|
||||
div("col-4") {
|
||||
output.attach(div { id = "output-$name" }){ 300}
|
||||
div("col-6") {
|
||||
output.attach(div { id = "output-$name" }) { 500 }
|
||||
hr()
|
||||
h2 { +(meta["title"].string ?: name.toString()) }
|
||||
}
|
||||
|
@ -5,8 +5,10 @@ import hep.dataforge.context.Context
|
||||
import hep.dataforge.context.PluginFactory
|
||||
import hep.dataforge.context.PluginTag
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.toName
|
||||
import hep.dataforge.vis.spatial.ThreePlugin
|
||||
import hep.dataforge.vis.spatial.three.ThreeFactory
|
||||
import hep.dataforge.vis.spatial.three.ThreePlugin
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
class GDMLPlugin : AbstractPlugin() {
|
||||
@ -14,21 +16,13 @@ class GDMLPlugin : AbstractPlugin() {
|
||||
|
||||
override fun dependsOn() = listOf(ThreePlugin)
|
||||
|
||||
override fun attach(context: Context) {
|
||||
super.attach(context)
|
||||
context.plugins.get<ThreePlugin>()?.factories?.apply {
|
||||
this["gdml".toName()] = ThreeGDMLFactory
|
||||
override fun provideTop(target: String): Map<Name, Any> {
|
||||
return when(target){
|
||||
ThreeFactory.TYPE-> mapOf("gdml".toName() to 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> {
|
||||
override val tag = PluginTag("vis.gdml", "hep.dataforge")
|
||||
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.DisplayObject
|
||||
import hep.dataforge.vis.common.int
|
||||
import hep.dataforge.vis.spatial.MeshThreeFactory
|
||||
import hep.dataforge.vis.spatial.three.MeshThreeFactory
|
||||
import hep.dataforge.vis.spatial.jsroot.createCubeBuffer
|
||||
import hep.dataforge.vis.spatial.jsroot.createGeometry
|
||||
import hep.dataforge.vis.spatial.jsroot.createTubeBuffer
|
||||
|
@ -2,9 +2,11 @@ package hep.dataforge.vis.spatial.jsroot
|
||||
|
||||
import hep.dataforge.context.Global
|
||||
import hep.dataforge.meta.EmptyMeta
|
||||
import hep.dataforge.vis.spatial.ThreeOutput
|
||||
import hep.dataforge.vis.spatial.three.ThreeOutput
|
||||
import hep.dataforge.vis.spatial.demo.ApplicationBase
|
||||
import hep.dataforge.vis.spatial.render
|
||||
import hep.dataforge.vis.spatial.three.ThreePlugin
|
||||
import hep.dataforge.vis.spatial.three.output
|
||||
import org.w3c.dom.HTMLDivElement
|
||||
import org.w3c.dom.events.Event
|
||||
import org.w3c.files.FileList
|
||||
@ -59,7 +61,7 @@ class JSRootDemoApp : ApplicationBase() {
|
||||
FileReader().apply {
|
||||
onload = {
|
||||
val string = result as String
|
||||
val renderer = ThreeOutput(Global)
|
||||
val renderer = Global.plugins.fetch(ThreePlugin).output()
|
||||
val canvas = document.getElementById("canvas")!!
|
||||
canvas.clear()
|
||||
renderer.attach(canvas)
|
||||
|
@ -5,7 +5,7 @@ import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.buildMeta
|
||||
import hep.dataforge.meta.toDynamic
|
||||
import hep.dataforge.vis.common.*
|
||||
import hep.dataforge.vis.spatial.MeshThreeFactory
|
||||
import hep.dataforge.vis.spatial.three.MeshThreeFactory
|
||||
import info.laht.threekt.core.BufferGeometry
|
||||
|
||||
class JSRootGeometry(parent: DisplayObject?, meta: Meta) : DisplayLeaf(parent, meta) {
|
||||
|
@ -7,7 +7,7 @@ import hep.dataforge.vis.common.DisplayGroup
|
||||
import hep.dataforge.vis.common.DisplayLeaf
|
||||
import hep.dataforge.vis.common.DisplayObject
|
||||
import hep.dataforge.vis.common.node
|
||||
import hep.dataforge.vis.spatial.ThreeFactory
|
||||
import hep.dataforge.vis.spatial.three.ThreeFactory
|
||||
import info.laht.threekt.core.Object3D
|
||||
|
||||
class JSRootObject(parent: DisplayObject?, meta: Meta, val data: dynamic) : DisplayLeaf(parent, meta) {
|
||||
|
@ -1,34 +1,29 @@
|
||||
package hep.dataforge.vis.spatial.jsroot
|
||||
|
||||
import hep.dataforge.context.AbstractPlugin
|
||||
import hep.dataforge.context.Context
|
||||
import hep.dataforge.context.PluginFactory
|
||||
import hep.dataforge.context.PluginTag
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.toName
|
||||
import hep.dataforge.vis.spatial.ThreePlugin
|
||||
import hep.dataforge.vis.spatial.three.ThreeFactory
|
||||
import hep.dataforge.vis.spatial.three.ThreePlugin
|
||||
|
||||
class JSRootPlugin : AbstractPlugin() {
|
||||
override val tag: PluginTag get() = JSRootPlugin.tag
|
||||
|
||||
override fun dependsOn() = listOf(ThreePlugin)
|
||||
|
||||
override fun attach(context: Context) {
|
||||
super.attach(context)
|
||||
context.plugins.get<ThreePlugin>()?.factories?.apply {
|
||||
this["jsRoot.geometry".toName()] = ThreeJSRootGeometryFactory
|
||||
this["jsRoot.object".toName()] = ThreeJSRootObjectFactory
|
||||
override fun provideTop(target: String): Map<Name, Any> {
|
||||
return when(target){
|
||||
ThreeFactory.TYPE -> mapOf(
|
||||
"jsRoot.geometry".toName() to ThreeJSRootGeometryFactory,
|
||||
"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> {
|
||||
override val tag = PluginTag("vis.jsroot", "hep.dataforge")
|
||||
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.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.names.startsWith
|
||||
@ -7,14 +7,14 @@ import hep.dataforge.provider.Type
|
||||
import hep.dataforge.vis.common.DisplayObject
|
||||
import hep.dataforge.vis.common.getProperty
|
||||
import hep.dataforge.vis.common.onChange
|
||||
import hep.dataforge.vis.spatial.ThreeFactory.Companion.TYPE
|
||||
import hep.dataforge.vis.spatial.ThreeFactory.Companion.buildMesh
|
||||
import hep.dataforge.vis.spatial.three.ConvexBufferGeometry
|
||||
import hep.dataforge.vis.spatial.three.EdgesGeometry
|
||||
import hep.dataforge.vis.spatial.three.euler
|
||||
import hep.dataforge.vis.spatial.*
|
||||
import hep.dataforge.vis.spatial.three.ThreeFactory.Companion.TYPE
|
||||
import hep.dataforge.vis.spatial.three.ThreeFactory.Companion.buildMesh
|
||||
import info.laht.threekt.core.BufferGeometry
|
||||
import info.laht.threekt.core.Object3D
|
||||
import info.laht.threekt.external.geometries.ConvexBufferGeometry
|
||||
import info.laht.threekt.geometries.BoxBufferGeometry
|
||||
import info.laht.threekt.geometries.EdgesGeometry
|
||||
import info.laht.threekt.geometries.WireframeGeometry
|
||||
import info.laht.threekt.objects.LineSegments
|
||||
import info.laht.threekt.objects.Mesh
|
||||
@ -35,7 +35,12 @@ interface ThreeFactory<T : DisplayObject> {
|
||||
companion object {
|
||||
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)
|
||||
|
||||
//inherited edges definition, enabled by default
|
||||
@ -49,6 +54,29 @@ interface ThreeFactory<T : DisplayObject> {
|
||||
val material = obj.getProperty("edges.material")?.material() ?: Materials.DEFAULT
|
||||
mesh.add(LineSegments(WireframeGeometry(mesh.geometry as BufferGeometry), material))
|
||||
}
|
||||
|
||||
//set position for meseh
|
||||
mesh.updatePosition(obj)
|
||||
|
||||
//add listener to object properties
|
||||
obj.onChange(this) { name, _, _ ->
|
||||
if (name.toString() == "material") {
|
||||
//updated material
|
||||
mesh.material = obj.material
|
||||
} else if (
|
||||
name.startsWith("pos".toName()) ||
|
||||
name.startsWith("scale".toName()) ||
|
||||
name.startsWith("rotation".toName()) ||
|
||||
name.toString() == "visible"
|
||||
) {
|
||||
//update position of mesh using this object
|
||||
mesh.updatePosition(obj)
|
||||
} else {
|
||||
//full update
|
||||
mesh.geometry = geometryBuilder(obj)
|
||||
mesh.material = obj.material
|
||||
}
|
||||
}
|
||||
return mesh
|
||||
}
|
||||
}
|
||||
@ -78,7 +106,8 @@ operator fun <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 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 {
|
||||
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
|
||||
val mesh = buildMesh(obj, geometry)
|
||||
|
||||
//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
|
||||
}
|
||||
}
|
||||
val mesh = buildMesh(obj, ::buildGeometry)
|
||||
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.get
|
||||
import hep.dataforge.meta.int
|
||||
import hep.dataforge.vis.spatial.three.toBufferGeometry
|
||||
import hep.dataforge.vis.spatial.GeometryBuilder
|
||||
import hep.dataforge.vis.spatial.Point3D
|
||||
import info.laht.threekt.core.BufferGeometry
|
||||
import info.laht.threekt.core.Face3
|
||||
import info.laht.threekt.core.Geometry
|
||||
import info.laht.threekt.math.Color
|
||||
import info.laht.threekt.math.Vector3
|
||||
|
||||
// TODO use unsafe cast instead
|
||||
@ -32,24 +32,18 @@ class ThreeGeometryBuilder : GeometryBuilder<BufferGeometry> {
|
||||
}
|
||||
|
||||
override fun face(vertex1: Point3D, vertex2: Point3D, vertex3: Point3D, normal: Point3D?, meta: Meta) {
|
||||
val materialIndex = meta["materialIndex"].int ?: 0
|
||||
val color = meta["color"]?.color() ?: Color()
|
||||
faces.add(
|
||||
Face3(
|
||||
append(vertex1),
|
||||
append(vertex2),
|
||||
append(vertex3),
|
||||
normal?.asVector() ?: Vector3(0, 0, 0),
|
||||
color,
|
||||
materialIndex
|
||||
)
|
||||
)
|
||||
val face = Face3(append(vertex1), append(vertex2), append(vertex3), normal?.asVector() ?: Vector3(0, 0, 0))
|
||||
meta["materialIndex"].int?.let { face.materialIndex = it }
|
||||
meta["color"]?.color()?.let { face.color = it }
|
||||
faces.add(face)
|
||||
}
|
||||
|
||||
override fun build(): BufferGeometry {
|
||||
return Geometry().apply {
|
||||
vertices = this@ThreeGeometryBuilder.vertices.map { it.asVector() }.toTypedArray()
|
||||
faces = this@ThreeGeometryBuilder.faces.toTypedArray()
|
||||
computeBoundingSphere()
|
||||
computeFaceNormals()
|
||||
}.toBufferGeometry()
|
||||
}
|
||||
}
|
@ -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 THREE from "three-full"
|
||||
import * as THREE from "three"
|
||||
import * as ThreeBSP from "ThreeCSG"
|
||||
|
||||
// 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,
|
||||
COPLANAR = 0,
|
||||
|
@ -22,9 +22,9 @@
|
||||
</div>
|
||||
<div class="container" id="canvas"></div>
|
||||
|
||||
<!--<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"-->
|
||||
<!-- integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"-->
|
||||
<!-- crossorigin="anonymous"></script>-->
|
||||
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
|
||||
integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
|
||||
crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"
|
||||
integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1"
|
||||
crossorigin="anonymous"></script>
|
||||
|
@ -26,12 +26,12 @@ class Box(parent: DisplayObject?, meta: Meta) : DisplayLeaf(parent, meta), Shape
|
||||
val node6 = Point3D(dx, -dy, dz)
|
||||
val node7 = Point3D(dx, dy, dz)
|
||||
val node8 = Point3D(-dx, dy, dz)
|
||||
geometryBuilder.face4(node1, node4, node3, node2, Point3D(0, 0, -1))
|
||||
geometryBuilder.face4(node1, node2, node6, node5, Point3D(0, -1, 0))
|
||||
geometryBuilder.face4(node2, node3, node7, node6, Point3D(1, 0, 0))
|
||||
geometryBuilder.face4(node4, node8, node7, node3, Point3D(0, 1, 0))
|
||||
geometryBuilder.face4(node1, node5, node8, node4, Point3D(-1, 0, 0))
|
||||
geometryBuilder.face4(node8, node5, node6, node7, Point3D(0, 0, 1))
|
||||
geometryBuilder.face4(node1, node4, node3, node2)
|
||||
geometryBuilder.face4(node1, node2, node6, node5)
|
||||
geometryBuilder.face4(node2, node3, node7, node6)
|
||||
geometryBuilder.face4(node4, node8, node7, node3)
|
||||
geometryBuilder.face4(node1, node5, node8, node4)
|
||||
geometryBuilder.face4(node8, node5, node6, node7)
|
||||
}
|
||||
|
||||
companion object {
|
||||
@ -41,3 +41,10 @@ class Box(parent: DisplayObject?, meta: Meta) : DisplayLeaf(parent, meta), Shape
|
||||
|
||||
fun DisplayGroup.box(meta: Meta = EmptyMeta, action: Box.() -> Unit = {}) =
|
||||
Box(this, meta).apply(action).also { add(it) }
|
||||
|
||||
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.Meta
|
||||
import hep.dataforge.meta.seal
|
||||
import hep.dataforge.vis.common.DisplayGroup
|
||||
import hep.dataforge.vis.common.DisplayLeaf
|
||||
import hep.dataforge.vis.common.DisplayObject
|
||||
|
||||
enum class CompositeType {
|
||||
UNION,
|
||||
INTERSECT,
|
||||
SUBTRACT
|
||||
}
|
||||
|
||||
class Composite(
|
||||
parent: DisplayObject?,
|
||||
val first: DisplayObject,
|
||||
val second: DisplayObject,
|
||||
val type: CompositeType = CompositeType.UNION,
|
||||
meta: Meta = EmptyMeta
|
||||
) : DisplayLeaf(parent,meta)
|
||||
) : 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{
|
||||
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 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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user