Added caching for geometries

This commit is contained in:
Alexander Nozik 2019-07-31 16:04:48 +03:00
parent 997a5a8e60
commit cf5a4fd7f6
29 changed files with 562 additions and 444 deletions

View File

@ -1,4 +1,4 @@
val dataforgeVersion by extra("0.1.3-dev-9") val dataforgeVersion by extra("0.1.3-dev-10")
plugins{ plugins{
val kotlinVersion = "1.3.50-eap-5" val kotlinVersion = "1.3.50-eap-5"

View File

@ -1,91 +0,0 @@
package hep.dataforge.vis.common
import hep.dataforge.meta.Config
import hep.dataforge.meta.Laminate
import hep.dataforge.meta.Meta
import hep.dataforge.names.Name
import hep.dataforge.provider.Provider
import kotlin.collections.set
/**
* A display group which allows both named and unnamed children
*/
open class VisualGroup(
override val parent: VisualObject? = null, tagRefs: Array<out Meta> = emptyArray()
) : VisualObject, Iterable<VisualObject>, Provider {
private val namedChildren = HashMap<Name, VisualObject>()
private val unnamedChildren = ArrayList<VisualObject>()
override val defaultTarget: String get() = VisualObject.TYPE
override val config = Config()
override val properties: Laminate by lazy {
combineProperties(parent, config, tagRefs)
}
override fun iterator(): Iterator<VisualObject> = (namedChildren.values + unnamedChildren).iterator()
override fun provideTop(target: String): Map<Name, Any> {
return when (target) {
VisualObject.TYPE -> namedChildren
else -> emptyMap()
}
}
private data class Listener(val owner: Any?, val callback: (Name?, VisualObject?) -> Unit)
private val listeners = HashSet<Listener>()
/**
* Add listener for children change
*/
fun onChildrenChange(owner: Any?, action: (Name?, VisualObject?) -> Unit) {
listeners.add(Listener(owner, action))
}
/**
* Remove children change listener
*/
fun removeChildrenChangeListener(owner: Any?) {
listeners.removeAll { it.owner === owner }
}
/**
* Add named or unnamed child to the group. If key is [null] the child is considered unnamed. Both key and value are not
* allowed to be null in the same time. If name is present and [child] is null, the appropriate element is removed.
*/
operator fun set(name: Name?, child: VisualObject?) {
when {
name != null -> {
if (child == null) {
namedChildren.remove(name)
} else {
namedChildren[name] = child
}
listeners.forEach { it.callback(name, child) }
}
child != null -> unnamedChildren.add(child)
else -> error("Both key and child element are empty")
}
}
operator fun set(key: String?, child: VisualObject?) = set(key?.asName(), child)
/**
* Append unnamed child
*/
fun add(child: VisualObject) {
unnamedChildren.add(child)
listeners.forEach { it.callback(null, child) }
}
/**
* remove unnamed child
*/
fun remove(child: VisualObject) {
unnamedChildren.remove(child)
listeners.forEach { it.callback(null, null) }
}
}

View File

@ -0,0 +1,29 @@
package hep.dataforge.vis.common
import hep.dataforge.meta.*
import hep.dataforge.names.Name
/**
* Basic [VisualObject] leaf element
*/
open class VisualLeaf(
parent: VisualObject? = null,
meta: Meta = EmptyMeta
) : AbstractVisualObject(parent), Configurable {
val properties = Styled(meta)
override val config: Config = properties.style
override fun setProperty(name: Name, value: Any?) {
config[name] = value
}
override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? {
return if (inherit) {
properties[name] ?: parent?.getProperty(name, inherit)
} else {
properties[name]
}
}
}

View File

@ -2,10 +2,11 @@ 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.get
import hep.dataforge.provider.Provider
import hep.dataforge.provider.Type import hep.dataforge.provider.Type
import hep.dataforge.vis.common.VisualObject.Companion.META_KEY
import hep.dataforge.vis.common.VisualObject.Companion.TAGS_KEY
import hep.dataforge.vis.common.VisualObject.Companion.TYPE import hep.dataforge.vis.common.VisualObject.Companion.TYPE
import kotlin.collections.set
private fun Laminate.withTop(meta: Meta): Laminate = Laminate(listOf(meta) + layers) private fun Laminate.withTop(meta: Meta): Laminate = Laminate(listOf(meta) + layers)
private fun Laminate.withBottom(meta: Meta): Laminate = Laminate(layers + meta) private fun Laminate.withBottom(meta: Meta): Laminate = Laminate(layers + meta)
@ -16,88 +17,190 @@ private fun Laminate.withBottom(meta: Meta): Laminate = Laminate(layers + meta)
@Type(TYPE) @Type(TYPE)
interface VisualObject : MetaRepr, Configurable { interface VisualObject : MetaRepr, Configurable {
val type: String get() = this::class.simpleName ?: TYPE
/** /**
* 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.
*/ */
val parent: VisualObject? val parent: VisualObject?
/** /**
* Individual properties configurator * Set property for this object
*/ */
override val config: Config fun setProperty(name: Name, value: Any?)
/** /**
* All properties including inherited ones * Get property including or excluding parent properties
*/ */
val properties: Laminate fun getProperty(name: Name, inherit: Boolean = true): MetaItem<*>?
override fun toMeta(): Meta = buildMeta { /**
"type" to this::class * Manually trigger property changed event. If [name] is empty, notify that the whole object is changed
"properties" to properties */
} fun propertyChanged(name: Name, before: MetaItem<*>? = null, after: MetaItem<*>? = null): Unit
/**
* Add listener triggering on property change
*/
fun onPropertyChange(owner: Any?, action: (Name, before: MetaItem<*>?, after: MetaItem<*>?) -> Unit): Unit
/**
* Remove change listeners with given owner.
*/
fun removeChangeListener(owner: Any?)
companion object { companion object {
const val TYPE = "visual" const val TYPE = "visual"
const val DEFAULT_TYPE = "" //const val META_KEY = "@meta"
//const val TYPE_KEY = "@type" //const val TAGS_KEY = "@tags"
//const val CHILDREN_KEY = "@children"
const val META_KEY = "@meta"
const val TAGS_KEY = "@tags"
} }
} }
/** internal data class MetaListener(
* A change listener for [VisualObject] configuration. val owner: Any? = null,
*/ val action: (name: Name, oldItem: MetaItem<*>?, newItem: MetaItem<*>?) -> Unit
fun VisualObject.onChange(owner: Any?, action: (Name, before: MetaItem<*>?, after: MetaItem<*>?) -> Unit) { )
config.onChange(owner, action)
parent?.onChange(owner, action)
}
/**
* Remove all meta listeners with matching owners
*/
fun VisualObject.removeChangeListener(owner: Any?) {
config.removeListener(owner)
parent?.removeChangeListener(owner)
}
/** abstract class AbstractVisualObject(override val parent: VisualObject?) : VisualObject {
* Additional meta not relevant to display private val listeners = HashSet<MetaListener>()
*/
val VisualObject.meta: Meta get() = config[META_KEY]?.node ?: EmptyMeta
val VisualObject.tags: List<String> get() = config[TAGS_KEY].stringList override fun propertyChanged(name: Name, before: MetaItem<*>?, after: MetaItem<*>?) {
for (l in listeners) {
l.action(name, before, after)
}
}
/** override fun onPropertyChange(owner: Any?, action: (Name, before: MetaItem<*>?, after: MetaItem<*>?) -> Unit) {
* Basic [VisualObject] leaf element listeners.add(MetaListener(owner, action))
*/ }
open class VisualLeaf(
final override val parent: VisualObject?,
tagRefs: Array<out Meta>
) : VisualObject {
override val config = Config()
override val properties: Laminate by lazy { override fun removeChangeListener(owner: Any?) {
combineProperties(parent, config, tagRefs) listeners.removeAll { it.owner == owner }
}
private var _config: Config? = null
override val config: Config get() = _config ?: Config().also { _config = it }
override fun setProperty(name: Name, value: Any?) {
config[name] = value
}
override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? {
return if (inherit) {
_config?.get(name) ?: parent?.getProperty(name, inherit)
} else {
_config?.get(name)
}
}
protected open fun MetaBuilder.updateMeta() {}
override fun toMeta(): Meta = buildMeta {
"type" to type
"properties" to _config
updateMeta()
} }
} }
internal fun combineProperties(parent: VisualObject?, config: Config, tagRefs: Array<out Meta>): Laminate { open class VisualGroup<T : VisualObject>(parent: VisualObject?) : AbstractVisualObject(parent), Iterable<T>, Provider {
val list = ArrayList<Meta>(tagRefs.size + 2)
list += config protected val namedChildren = HashMap<Name, T>()
list.addAll(tagRefs) protected val unnamedChildren = ArrayList<T>()
parent?.properties?.let { list.add(it) }
return Laminate(list) override val defaultTarget: String get() = VisualObject.TYPE
override fun iterator(): Iterator<T> = (namedChildren.values + unnamedChildren).iterator()
override fun provideTop(target: String): Map<Name, Any> {
return when (target) {
TYPE -> namedChildren
else -> emptyMap()
}
}
private data class Listener<T : VisualObject>(val owner: Any?, val callback: (Name?, T?) -> Unit)
private val listeners = HashSet<Listener<T>>()
/**
* Add listener for children change
*/
fun onChildrenChange(owner: Any?, action: (Name?, T?) -> Unit) {
listeners.add(Listener(owner, action))
}
/**
* Remove children change listener
*/
fun removeChildrenChangeListener(owner: Any?) {
listeners.removeAll { it.owner === owner }
}
/**
* Add named or unnamed child to the group. If key is [null] the child is considered unnamed. Both key and value are not
* allowed to be null in the same time. If name is present and [child] is null, the appropriate element is removed.
*/
operator fun set(name: Name?, child: T?) {
when {
name != null -> {
if (child == null) {
namedChildren.remove(name)
} else {
namedChildren[name] = child
}
listeners.forEach { it.callback(name, child) }
}
child != null -> unnamedChildren.add(child)
else -> error("Both key and child element are empty")
}
}
operator fun set(key: String?, child: T?) = set(key?.asName(), child)
/**
* Get named child by name
*/
operator fun get(name: Name): T? = namedChildren[name]
/**
* Get named child by string
*/
operator fun get(key: String): T? = namedChildren[key]
/**
* Get an unnamed child
*/
operator fun get(index: Int): T? = unnamedChildren[index]
/**
* Append unnamed child
*/
fun add(child: T) {
unnamedChildren.add(child)
listeners.forEach { it.callback(null, child) }
}
/**
* remove unnamed child
*/
fun remove(child: VisualObject) {
unnamedChildren.remove(child)
listeners.forEach { it.callback(null, null) }
}
protected fun MetaBuilder.updateChildren() {
//adding unnamed children
"children" to unnamedChildren.map { it.toMeta() }
//adding named children
namedChildren.forEach {
"children[${it.key}" to it.value.toMeta()
}
}
override fun MetaBuilder.updateMeta() {
updateChildren()
}
} }
///**
// * A group that could contain both named and unnamed children. Unnamed children could be accessed only via
// */
//interface VisualGroup : DisplayObject, Iterable<DisplayObject>, Provider {
// override val defaultTarget: String get() = DisplayObject.TARGET
//
// val children
//}

View File

@ -23,7 +23,7 @@ class DisplayObjectDelegate(
override fun getValue(thisRef: VisualObject, property: KProperty<*>): MetaItem<*>? { override fun getValue(thisRef: VisualObject, property: KProperty<*>): MetaItem<*>? {
val name = key ?: property.name.asName() val name = key ?: property.name.asName()
return if (inherited) { return if (inherited) {
thisRef.properties[name] thisRef.getProperty(name)
} else { } else {
thisRef.config[name] thisRef.config[name]
} ?: default } ?: default
@ -48,7 +48,7 @@ class DisplayObjectDelegateWrapper<T>(
override fun getValue(thisRef: VisualObject, property: KProperty<*>): T { override fun getValue(thisRef: VisualObject, property: KProperty<*>): T {
val name = key ?: property.name.asName() val name = key ?: property.name.asName()
return if (inherited) { return if (inherited) {
read(thisRef.properties[name]) read(thisRef.getProperty(name))
} else { } else {
read(thisRef.config[name]) read(thisRef.config[name])
} ?: default } ?: default

View File

@ -3,7 +3,7 @@ package hep.dataforge.vis.spatial
import hep.dataforge.context.Context import hep.dataforge.context.Context
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.output.Output import hep.dataforge.output.Output
import hep.dataforge.vis.common.VisualGroup import hep.dataforge.vis.common.VisualNode
import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.common.VisualObject
import javafx.scene.Group import javafx.scene.Group
import javafx.scene.Node import javafx.scene.Node
@ -27,7 +27,7 @@ class FX3DOutput(override val context: Context) : Output<VisualObject> {
org.fxyz3d.geometry.Point3D(x.value ?: 0f, y.value ?: 0f, z.value ?: 0f) org.fxyz3d.geometry.Point3D(x.value ?: 0f, y.value ?: 0f, z.value ?: 0f)
} }
return when (obj) { return when (obj) {
is VisualGroup -> Group(obj.map { buildNode(it) }).apply { is VisualNode -> Group(obj.map { buildNode(it) }).apply {
this.translateXProperty().bind(x) this.translateXProperty().bind(x)
this.translateYProperty().bind(y) this.translateYProperty().bind(y)
this.translateZProperty().bind(z) this.translateZProperty().bind(z)

View File

@ -2,7 +2,6 @@ package hep.dataforge.vis.spatial
import hep.dataforge.context.Global import hep.dataforge.context.Global
import hep.dataforge.meta.number import hep.dataforge.meta.number
import hep.dataforge.vis.common.VisualGroup
import javafx.scene.Parent import javafx.scene.Parent
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@ -21,7 +20,7 @@ class RendererDemoView : View() {
center = renderer.canvas.root center = renderer.canvas.root
} }
lateinit var group: VisualGroup lateinit var group: VisualGroup3D
init { init {

View File

@ -0,0 +1,60 @@
package hep.dataforge.vis.spatial.gdml
import hep.dataforge.meta.Meta
import hep.dataforge.meta.buildMeta
import hep.dataforge.meta.builder
import hep.dataforge.vis.spatial.VisualGroup3D
import scientifik.gdml.GDML
import scientifik.gdml.GDMLGroup
import scientifik.gdml.GDMLMaterial
import scientifik.gdml.GDMLSolid
import kotlin.random.Random
class GDMLTransformer(val root: GDML) {
private val materialCache = HashMap<GDMLMaterial, Meta>()
private val random = Random(111)
/**
* A special group for local templates
*/
val templates by lazy { VisualGroup3D() }
var lUnit: LUnit = LUnit.MM
var resolveColor: ColorResolver = { material, _ ->
val materialColor = materialCache.getOrPut(material) {
buildMeta {
"color" to random.nextInt(0, Int.MAX_VALUE)
}
}
if (this?.physVolumes?.isEmpty() != false) {
materialColor
} else {
materialColor.builder().apply { "opacity" to 0.5 }
}
}
var acceptSolid: (GDMLSolid) -> Boolean = { true }
var acceptGroup: (GDMLGroup) -> Boolean = { true }
fun printStatistics() {
println("Solids:")
solidCounter.entries.sortedByDescending { it.value }.forEach {
println("\t$it")
}
println(println("Solids total: ${solidCounter.values.sum()}"))
}
private val solidCounter = HashMap<String, Int>()
internal fun solidAdded(solid: GDMLSolid) {
solidCounter[solid.name] = (solidCounter[solid.name] ?: 0) + 1
}
var onFinish: GDMLTransformer.() -> Unit = {}
internal fun finished() {
onFinish(this)
}
}

View File

@ -1,58 +1,10 @@
package hep.dataforge.vis.spatial.gdml package hep.dataforge.vis.spatial.gdml
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.meta.buildMeta
import hep.dataforge.meta.builder
import hep.dataforge.vis.spatial.* import hep.dataforge.vis.spatial.*
import scientifik.gdml.* import scientifik.gdml.*
import kotlin.math.cos import kotlin.math.cos
import kotlin.math.sin import kotlin.math.sin
import kotlin.random.Random
class GDMLTransformer(val root: GDML) {
private val cache = HashMap<GDMLMaterial, Meta>()
private val random = Random(111)
var lUnit: LUnit = LUnit.MM
var resolveColor: ColorResolver = { material, _ ->
val materialColor = cache.getOrPut(material) {
buildMeta {
"color" to random.nextInt(0, Int.MAX_VALUE)
}
}
if (this?.physVolumes?.isEmpty() != false) {
materialColor
} else {
materialColor.builder().apply { "opacity" to 0.5 }
}
}
var acceptSolid: (GDMLSolid) -> Boolean = { true }
var acceptGroup: (GDMLGroup) -> Boolean = { true }
fun printStatistics() {
println("Solids:")
solidCounter.entries.sortedByDescending { it.value }.forEach {
println("\t$it")
}
println(println("Solids total: ${solidCounter.values.sum()}"))
}
private val solidCounter = HashMap<String, Int>()
internal fun solidAdded(solid: GDMLSolid) {
solidCounter[solid.name] = (solidCounter[solid.name] ?: 0) + 1
}
var onFinish: GDMLTransformer.() -> Unit = {}
internal fun finished(){
onFinish(this)
}
}
private fun VisualObject3D.withPosition( private fun VisualObject3D.withPosition(
@ -82,7 +34,6 @@ private fun VisualObject3D.withPosition(
private inline operator fun Number.times(d: Double) = toDouble() * d private inline operator fun Number.times(d: Double) = toDouble() * d
private inline operator fun Number.times(f: Float) = toFloat() * f private inline operator fun Number.times(f: Float) = toFloat() * f
private fun VisualGroup3D.addSolid( private fun VisualGroup3D.addSolid(
context: GDMLTransformer, context: GDMLTransformer,
solid: GDMLSolid, solid: GDMLSolid,
@ -238,9 +189,12 @@ private fun volume(
val material = group.materialref.resolve(context.root) ?: GDMLElement(group.materialref.ref) val material = group.materialref.resolve(context.root) ?: GDMLElement(group.materialref.ref)
if (context.acceptSolid(solid)) { if (context.acceptSolid(solid)) {
addSolid(context, solid, solid.name) { val cachedSolid = context.templates[solid.name]
this.material = context.resolveColor(group, material, solid) ?: context.templates.addSolid(context, solid, solid.name) {
} this.material = context.resolveColor(group, material, solid)
}
val wrapper = Proxy3D(this,cachedSolid)
add(wrapper)
} }
when (val vol = group.placement) { when (val vol = group.placement) {
@ -262,7 +216,7 @@ fun GDML.toVisual(block: GDMLTransformer.() -> Unit = {}): VisualGroup3D {
val context = GDMLTransformer(this).apply(block) val context = GDMLTransformer(this).apply(block)
return volume(context, world).also{ return volume(context, world).also {
context.finished() context.finished()
} }
} }

View File

@ -90,11 +90,11 @@ private class GDMLDemoApp : ApplicationBase() {
launch { message("Converting GDML into DF-VIS format") } launch { message("Converting GDML into DF-VIS format") }
val visual = gdml.toVisual { val visual = gdml.toVisual {
lUnit = LUnit.CM lUnit = LUnit.CM
acceptSolid = { solid -> // acceptSolid = { solid ->
!solid.name.startsWith("ecal") // !solid.name.startsWith("ecal")
&& !solid.name.startsWith("V") // && !solid.name.startsWith("V")
&& !solid.name.startsWith("U") // && !solid.name.startsWith("U")
} // }
} }
launch { message("Rendering") } launch { message("Rendering") }
val output = three.output(canvas) val output = three.output(canvas)

View File

@ -18,7 +18,7 @@ fun main() {
val xml = GDML.format.parse(GDML.serializer(), xmlReader) val xml = GDML.format.parse(GDML.serializer(), xmlReader)
xml.toVisual { xml.toVisual {
lUnit = LUnit.CM lUnit = LUnit.CM
acceptSolid = { solid -> !solid.name.startsWith("ecal") && !solid.name.startsWith("V") } //acceptSolid = { solid -> !solid.name.startsWith("ecal") && !solid.name.startsWith("V") }
onFinish = { printStatistics() } onFinish = { printStatistics() }
} }
readLine() readLine()

View File

@ -40,21 +40,24 @@ fun MetaItem<*>.color(): Color {
} }
} }
private val materialCache = HashMap<Meta, Material>()
/** /**
* Infer Three material based on meta item * Infer Three material based on meta item
*/ */
fun Meta?.jsMaterial(): Material { fun Meta?.jsMaterial(): Material {
return if(this == null){ return if (this == null) {
Materials.DEFAULT Materials.DEFAULT
} else } else
//TODO add more oprions for material //TODO add more options for material
MeshBasicMaterial().apply { return materialCache.getOrPut(this) {
color = get("color")?.color()?: Materials.DEFAULT_COLOR MeshBasicMaterial().apply {
opacity = get("opacity")?.double ?: 1.0 color = get("color")?.color() ?: Materials.DEFAULT_COLOR
transparent = get("transparent").boolean ?: (opacity < 1.0) opacity = get("opacity")?.double ?: 1.0
//node["specularColor"]?.let { specular = it.color() } transparent = get("transparent").boolean ?: (opacity < 1.0)
side = 2 //node["specularColor"]?.let { specular = it.color() }
side = 2
}
} }
} }

View File

@ -18,7 +18,7 @@ class ThreeCompositeFactory(val three: ThreePlugin) : MeshThreeFactory<Composite
second.updateMatrix() second.updateMatrix()
val firstCSG = CSG.fromMesh(first) val firstCSG = CSG.fromMesh(first)
val secondCSG = CSG.fromMesh(second) val secondCSG = CSG.fromMesh(second)
val resultCSG = when (obj.type) { val resultCSG = when (obj.compositeType) {
CompositeType.UNION -> firstCSG.union(secondCSG) CompositeType.UNION -> firstCSG.union(secondCSG)
CompositeType.INTERSECT -> firstCSG.intersect(secondCSG) CompositeType.INTERSECT -> firstCSG.intersect(secondCSG)
CompositeType.SUBTRACT -> firstCSG.subtract(secondCSG) CompositeType.SUBTRACT -> firstCSG.subtract(secondCSG)

View File

@ -0,0 +1,11 @@
package hep.dataforge.vis.spatial.three
import hep.dataforge.vis.spatial.Convex
import info.laht.threekt.external.geometries.ConvexBufferGeometry
object ThreeConvexFactory : MeshThreeFactory<Convex>(Convex::class) {
override fun buildGeometry(obj: Convex): ConvexBufferGeometry {
val vectors = obj.points.map { it.asVector() }.toTypedArray()
return ConvexBufferGeometry(vectors)
}
}

View File

@ -1,18 +1,16 @@
package hep.dataforge.vis.spatial.three package hep.dataforge.vis.spatial.three
import hep.dataforge.meta.boolean import hep.dataforge.meta.boolean
import hep.dataforge.meta.get
import hep.dataforge.meta.int import hep.dataforge.meta.int
import hep.dataforge.meta.node import hep.dataforge.meta.node
import hep.dataforge.names.plus
import hep.dataforge.names.startsWith import hep.dataforge.names.startsWith
import hep.dataforge.provider.Type import hep.dataforge.provider.Type
import hep.dataforge.vis.common.onChange import hep.dataforge.vis.common.asName
import hep.dataforge.vis.spatial.* import hep.dataforge.vis.spatial.*
import hep.dataforge.vis.spatial.three.ThreeFactory.Companion.TYPE 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.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.EdgesGeometry 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
@ -31,55 +29,6 @@ interface ThreeFactory<T : VisualObject3D> {
companion object { companion object {
const val TYPE = "threeFactory" const val TYPE = "threeFactory"
fun <T : VisualObject3D> 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.jsMaterial())
//inherited edges definition, enabled by default
if (obj.properties["edges.enabled"].boolean != false) {
val material = obj.properties["edges.material"].node?.jsMaterial() ?: Materials.DEFAULT
mesh.add(LineSegments(EdgesGeometry(mesh.geometry as BufferGeometry), material))
}
//inherited wireframe definition, disabled by default
if (obj.properties["wireframe.enabled"].boolean == true) {
val material = obj.properties["wireframe.material"].node?.jsMaterial() ?: Materials.DEFAULT
mesh.add(LineSegments(WireframeGeometry(mesh.geometry as BufferGeometry), material))
}
//set position for mesh
mesh.updatePosition(obj)
obj.config["layer"].int?.let {
mesh.layers.set(it)
}
//add listener to object properties
obj.onChange(this) { name, _, _ ->
if (name.startsWith(VisualObject3D.materialKey)) {
//updated material
mesh.material = obj.material.jsMaterial()
} else if (
name.startsWith(VisualObject3D.position) ||
name.startsWith(VisualObject3D.rotation) ||
name.startsWith(VisualObject3D.scale) ||
name == VisualObject3D.visibleKey
) {
//update position of mesh using this object
mesh.updatePosition(obj)
} else {
//full update
mesh.geometry = geometryBuilder(obj)
mesh.material = obj.material.jsMaterial()
}
}
return mesh
}
} }
} }
@ -90,7 +39,30 @@ internal fun Object3D.updatePosition(obj: VisualObject3D) {
position.set(obj.x, obj.y, obj.z) position.set(obj.x, obj.y, obj.z)
setRotationFromEuler(obj.euler) setRotationFromEuler(obj.euler)
scale.set(obj.scaleX, obj.scaleY, obj.scaleZ) scale.set(obj.scaleX, obj.scaleY, obj.scaleZ)
visible = obj.visible ?: true updateMatrix()
}
internal fun <T : VisualObject3D> Mesh.updateFrom(obj: T) {
matrixAutoUpdate = false
//inherited edges definition, enabled by default
if (obj.getProperty(MeshThreeFactory.EDGES_ENABLED_KEY).boolean != false) {
val material = obj.getProperty(MeshThreeFactory.EDGES_MATERIAL_KEY).node?.jsMaterial() ?: Materials.DEFAULT
add(LineSegments(EdgesGeometry(geometry as BufferGeometry), material))
}
//inherited wireframe definition, disabled by default
if (obj.getProperty(MeshThreeFactory.WIREFRAME_ENABLED_KEY).boolean == true) {
val material = obj.getProperty(MeshThreeFactory.WIREFRAME_MATERIAL_KEY).node?.jsMaterial() ?: Materials.DEFAULT
add(LineSegments(WireframeGeometry(geometry as BufferGeometry), material))
}
//set position for mesh
updatePosition(obj)
obj.getProperty(MeshThreeFactory.LAYER_KEY).int?.let {
layers.set(it)
}
} }
/** /**
@ -118,6 +90,51 @@ abstract class MeshThreeFactory<T : VisualObject3D>(override val type: KClass<ou
//create mesh from geometry //create mesh from geometry
return buildMesh<T>(obj) { buildGeometry(it) } return buildMesh<T>(obj) { buildGeometry(it) }
} }
companion object {
val EDGES_KEY = "edges".asName()
val WIREFRAME_KEY = "wireframe".asName()
val ENABLED_KEY = "enabled".asName()
val EDGES_ENABLED_KEY = EDGES_KEY + ENABLED_KEY
val EDGES_MATERIAL_KEY = EDGES_KEY + VisualObject3D.MATERIAL_KEY
val WIREFRAME_ENABLED_KEY = WIREFRAME_KEY + ENABLED_KEY
val WIREFRAME_MATERIAL_KEY = WIREFRAME_KEY + VisualObject3D.MATERIAL_KEY
val LAYER_KEY = "layer".asName()
fun <T : VisualObject3D> buildMesh(obj: T, geometryBuilder: (T) -> BufferGeometry): Mesh {
//TODO add caching for geometries using templates
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.jsMaterial())
mesh.updateFrom(obj)
//add listener to object properties
obj.onPropertyChange(this) { name, _, _ ->
if (name.startsWith(VisualObject3D.MATERIAL_KEY)) {
//updated material
mesh.material = obj.material.jsMaterial()
} else if (
name.startsWith(VisualObject3D.position) ||
name.startsWith(VisualObject3D.rotation) ||
name.startsWith(VisualObject3D.scale)
) {
//update position of mesh using this object
mesh.updatePosition(obj)
} else if (name == VisualObject3D.VISIBLE_KEY) {
obj.visible = obj.visible ?: true
} else {
//full update
mesh.geometry = geometryBuilder(obj)
mesh.material = obj.material.jsMaterial()
}
}
return mesh
}
}
} }
/** /**
@ -129,12 +146,4 @@ object ThreeShapeFactory : MeshThreeFactory<Shape>(Shape::class) {
ThreeGeometryBuilder().apply { toGeometry(this) }.build() ThreeGeometryBuilder().apply { toGeometry(this) }.build()
} }
} }
}
//FIXME not functional yet
object ThreeConvexFactory : MeshThreeFactory<Convex>(Convex::class) {
override fun buildGeometry(obj: Convex): ConvexBufferGeometry {
val vectors = obj.points.map { it.asVector() }.toTypedArray()
return ConvexBufferGeometry(vectors)
}
} }

View File

@ -20,6 +20,7 @@ class ThreePlugin : AbstractPlugin() {
private val objectFactories = HashMap<KClass<out VisualObject3D>, ThreeFactory<*>>() private val objectFactories = HashMap<KClass<out VisualObject3D>, ThreeFactory<*>>()
private val compositeFactory = ThreeCompositeFactory(this) private val compositeFactory = ThreeCompositeFactory(this)
private val proxyFactory = ThreeProxyFactory(this)
init { init {
//Add specialized factories here //Add specialized factories here
@ -38,7 +39,6 @@ class ThreePlugin : AbstractPlugin() {
return when (obj) { return when (obj) {
is VisualGroup3D -> Group(obj.mapNotNull { is VisualGroup3D -> Group(obj.mapNotNull {
try { try {
it as VisualObject3D
buildObject3D(it) buildObject3D(it)
} catch (ex: Throwable) { } catch (ex: Throwable) {
console.error(ex) console.error(ex)
@ -49,6 +49,7 @@ class ThreePlugin : AbstractPlugin() {
updatePosition(obj) updatePosition(obj)
} }
is Composite -> compositeFactory(obj) is Composite -> compositeFactory(obj)
is Proxy3D -> proxyFactory(obj)
else -> { else -> {
//find specialized factory for this type if it is present //find specialized factory for this type if it is present
val factory = findObjectFactory(obj::class) val factory = findObjectFactory(obj::class)

View File

@ -0,0 +1,25 @@
package hep.dataforge.vis.spatial.three
import hep.dataforge.vis.spatial.Proxy3D
import hep.dataforge.vis.spatial.VisualObject3D
import info.laht.threekt.core.BufferGeometry
import info.laht.threekt.core.Object3D
import info.laht.threekt.objects.Mesh
class ThreeProxyFactory(val three: ThreePlugin) : ThreeFactory<Proxy3D> {
private val cache = HashMap<VisualObject3D, Mesh>()
override val type = Proxy3D::class
override fun invoke(obj: Proxy3D): Object3D {
val templateMesh = cache.getOrPut(obj.template) {
three.buildObject3D(obj.template) as Mesh
}
val mesh = Mesh(templateMesh.geometry as BufferGeometry, templateMesh.material)
//val mesh = templateMesh.clone()
mesh.updatePosition(obj)
return mesh
}
}

View File

@ -1,11 +1,9 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.meta.Meta
import hep.dataforge.vis.common.VisualGroup
import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.common.VisualObject
class Box(parent: VisualObject?, val xSize: Number, val ySize: Number, val zSize: Number, meta: Array<out Meta>) : class Box(parent: VisualObject?, val xSize: Number, val ySize: Number, val zSize: Number) :
VisualLeaf3D(parent, meta), Shape { VisualLeaf3D(parent), Shape {
//TODO add helper for color configuration //TODO add helper for color configuration
override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) { override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) {
@ -34,14 +32,10 @@ class Box(parent: VisualObject?, val xSize: Number, val ySize: Number, val zSize
} }
} }
//fun VisualGroup.box(meta: Meta = EmptyMeta, action: Box.() -> Unit = {}) = inline fun VisualGroup3D.box(
// Box(this, meta).apply(action).also { add(it) }
inline fun VisualGroup.box(
xSize: Number, xSize: Number,
ySize: Number, ySize: Number,
zSize: Number, zSize: Number,
name: String? = null, name: String? = null,
vararg meta: Meta,
action: Box.() -> Unit = {} action: Box.() -> Unit = {}
) = Box(this, xSize, ySize, zSize, meta).apply(action).also { set(name, it) } ) = Box(this, xSize, ySize, zSize).apply(action).also { set(name, it) }

View File

@ -1,9 +1,7 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.meta.Meta
import hep.dataforge.meta.isEmpty import hep.dataforge.meta.isEmpty
import hep.dataforge.meta.update import hep.dataforge.meta.update
import hep.dataforge.vis.common.VisualGroup
import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.common.VisualObject
enum class CompositeType { enum class CompositeType {
@ -16,21 +14,19 @@ open class Composite(
parent: VisualObject?, parent: VisualObject?,
val first: VisualObject3D, val first: VisualObject3D,
val second: VisualObject3D, val second: VisualObject3D,
val type: CompositeType = CompositeType.UNION, val compositeType: CompositeType = CompositeType.UNION
meta: Array<out Meta> ) : VisualLeaf3D(parent)
) : VisualLeaf3D(parent, meta)
fun VisualGroup.composite( fun VisualGroup3D.composite(
type: CompositeType, type: CompositeType,
name: String? = null, name: String? = null,
vararg meta: Meta,
builder: VisualGroup3D.() -> Unit builder: VisualGroup3D.() -> Unit
): Composite { ): Composite {
val group = VisualGroup3D().apply(builder) val group = VisualGroup3D().apply(builder)
val children = group.filterIsInstance<VisualObject3D>() val children = group.filterIsInstance<VisualObject3D>()
if (children.size != 2) error("Composite requires exactly two children") if (children.size != 2) error("Composite requires exactly two children")
return Composite(this, children[0], children[1], type, meta).also { return Composite(this, children[0], children[1], type).also {
if( !group.config.isEmpty()) { if (!group.config.isEmpty()) {
it.config.update(group.config) it.config.update(group.config)
} }
it.position = group.position it.position = group.position
@ -41,11 +37,11 @@ fun VisualGroup.composite(
} }
} }
fun VisualGroup3D.union(name: String? = null, vararg meta: Meta, builder: VisualGroup3D.() -> Unit) = fun VisualGroup3D.union(name: String? = null, builder: VisualGroup3D.() -> Unit) =
composite(CompositeType.UNION, name, *meta, builder = builder) composite(CompositeType.UNION, name, builder = builder)
fun VisualGroup3D.subtract(name: String? = null, vararg meta: Meta, builder: VisualGroup3D.() -> Unit) = fun VisualGroup3D.subtract(name: String? = null, builder: VisualGroup3D.() -> Unit) =
composite(CompositeType.SUBTRACT, name, *meta, builder = builder) composite(CompositeType.SUBTRACT, name, builder = builder)
fun VisualGroup3D.intersect(name: String? = null, vararg meta: Meta, builder: VisualGroup3D.() -> Unit) = fun VisualGroup3D.intersect(name: String? = null, builder: VisualGroup3D.() -> Unit) =
composite(CompositeType.INTERSECT, name, *meta, builder = builder) composite(CompositeType.INTERSECT, name, builder = builder)

View File

@ -1,10 +1,8 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.meta.Meta
import hep.dataforge.vis.common.VisualGroup
import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.common.VisualObject
class Convex(parent: VisualObject?, val points: List<Point3D>, meta: Array<out Meta>) : VisualLeaf3D(parent, meta) { class Convex(parent: VisualObject?, val points: List<Point3D>) : VisualLeaf3D(parent) {
companion object { companion object {
@ -12,8 +10,8 @@ class Convex(parent: VisualObject?, val points: List<Point3D>, meta: Array<out M
} }
} }
fun VisualGroup.convex(vararg meta: Meta, action: ConvexBuilder.() -> Unit = {}) = fun VisualGroup3D.convex(action: ConvexBuilder.() -> Unit = {}) =
ConvexBuilder().apply(action).build(this, meta).also { add(it) } ConvexBuilder().apply(action).build(this).also { add(it) }
class ConvexBuilder { class ConvexBuilder {
private val points = ArrayList<Point3D>() private val points = ArrayList<Point3D>()
@ -22,7 +20,7 @@ class ConvexBuilder {
points.add(Point3D(x, y, z)) points.add(Point3D(x, y, z))
} }
fun build(parent: VisualObject?, meta: Array<out Meta>): Convex { fun build(parent: VisualObject?): Convex {
return Convex(parent, points, meta) return Convex(parent, points)
} }
} }

View File

@ -1,7 +1,5 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.meta.Meta
import hep.dataforge.vis.common.VisualGroup
import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.common.VisualObject
import hep.dataforge.vis.common.number import hep.dataforge.vis.common.number
import kotlin.math.PI import kotlin.math.PI
@ -9,21 +7,19 @@ import kotlin.math.PI
/** /**
* A cylinder or cut cone segment * A cylinder or cut cone segment
*/ */
class Cylinder(parent: VisualObject?, var radius: Number, var height: Number, meta: Array<out Meta>) : class Cylinder(parent: VisualObject?, var radius: Number, var height: Number) : VisualLeaf3D(parent) {
VisualLeaf3D(parent, meta) {
var upperRadius by number(radius) var upperRadius by number(radius)
var startAngle by number(0.0) var startAngle by number(0.0)
var angle by number(2 * PI) var angle by number(2 * PI)
} }
fun VisualGroup.cylinder( fun VisualGroup3D.cylinder(
r: Number, r: Number,
height: Number, height: Number,
name: String? = null, name: String? = null,
vararg meta: Meta,
block: Cylinder.() -> Unit = {} block: Cylinder.() -> Unit = {}
): Cylinder { ): Cylinder {
val cylinder = Cylinder(this, r, height, meta) val cylinder = Cylinder(this, r, height)
cylinder.apply(block) cylinder.apply(block)
return cylinder.also { set(name, it) } return cylinder.also { set(name, it) }
} }

View File

@ -1,7 +1,5 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.meta.Meta
import hep.dataforge.vis.common.VisualGroup
import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.common.VisualObject
import kotlin.math.PI import kotlin.math.PI
import kotlin.math.cos import kotlin.math.cos
@ -32,7 +30,7 @@ fun Shape2DBuilder.polygon(vertices: Int, radius: Number) {
data class Layer(var x: Number, var y: Number, var z: Number, var scale: Number) data class Layer(var x: Number, var y: Number, var z: Number, var scale: Number)
class Extruded(parent: VisualObject?, meta: Array<out Meta>) : VisualLeaf3D(parent, meta), Shape { class Extruded(parent: VisualObject?) : VisualLeaf3D(parent), Shape {
var shape: List<Point2D> = ArrayList() var shape: List<Point2D> = ArrayList()
@ -112,5 +110,5 @@ class Extruded(parent: VisualObject?, meta: Array<out Meta>) : VisualLeaf3D(pare
} }
} }
fun VisualGroup.extrude(name: String? = null, vararg meta: Meta, action: Extruded.() -> Unit = {}) = fun VisualGroup3D.extrude(name: String? = null, action: Extruded.() -> Unit = {}) =
Extruded(this, meta).apply(action).also { set(name, it) } Extruded(this).apply(action).also { set(name, it) }

View File

@ -0,0 +1,34 @@
package hep.dataforge.vis.spatial
import hep.dataforge.meta.MetaBuilder
import hep.dataforge.meta.MetaItem
import hep.dataforge.names.Name
import hep.dataforge.vis.common.AbstractVisualObject
import hep.dataforge.vis.common.VisualObject
/**
* A proxy [VisualObject3D] to reuse a [template] object
*/
class Proxy3D(parent: VisualObject?, val template: VisualObject3D) : AbstractVisualObject(parent), VisualObject3D {
override var position: Value3 = Value3()
override var rotation: Value3 = Value3()
override var scale: Value3 = Value3(1f, 1f, 1f)
override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? {
return if (inherit) {
super.getProperty(name, false) ?: template.getProperty(name, false) ?: parent?.getProperty(name, inherit)
} else {
super.getProperty(name, false) ?: template.getProperty(name, false)
}
}
override fun MetaBuilder.updateMeta() {
updatePosition()
}
}
inline fun VisualGroup3D.proxy(
template: VisualObject3D,
name: String? = null,
action: Proxy3D.() -> Unit = {}
) = Proxy3D(this, template).apply(action).also { set(name, it) }

View File

@ -1,26 +1,23 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.meta.Meta
import hep.dataforge.vis.common.VisualGroup
import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.common.VisualObject
import hep.dataforge.vis.common.number import hep.dataforge.vis.common.number
import kotlin.math.PI import kotlin.math.PI
class Sphere(parent: VisualObject?, var radius: Number, meta: Array<out Meta>) : VisualLeaf3D(parent, meta) { class Sphere(parent: VisualObject?, var radius: Number) : VisualLeaf3D(parent) {
var phiStart by number(0.0) var phiStart by number(0.0)
var phi by number(2 * PI) var phi by number(2 * PI)
var thetaStart by number(0.0) var thetaStart by number(0.0)
var theta by number(PI) var theta by number(PI)
} }
fun VisualGroup.sphere( fun VisualGroup3D.sphere(
radius: Number, radius: Number,
phi: Number = 2 * PI, phi: Number = 2 * PI,
theta: Number = PI, theta: Number = PI,
name: String? = null, name: String? = null,
vararg meta: Meta,
action: Sphere.() -> Unit = {} action: Sphere.() -> Unit = {}
) = Sphere(this, radius, meta).apply(action).apply { ) = Sphere(this, radius).apply(action).apply {
this.phi = phi.toDouble() this.phi = phi.toDouble()
this.theta = theta.toDouble() this.theta = theta.toDouble()
}.also { set(name, it) } }.also { set(name, it) }

View File

@ -1,16 +1,15 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.meta.* import hep.dataforge.meta.*
import hep.dataforge.names.Name
import hep.dataforge.names.plus import hep.dataforge.names.plus
import hep.dataforge.output.Output import hep.dataforge.output.Output
import hep.dataforge.vis.common.AbstractVisualObject
import hep.dataforge.vis.common.VisualGroup import hep.dataforge.vis.common.VisualGroup
import hep.dataforge.vis.common.VisualLeaf
import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.common.VisualObject
import hep.dataforge.vis.common.asName import hep.dataforge.vis.common.asName
import hep.dataforge.vis.spatial.VisualObject3D.Companion.detailKey import hep.dataforge.vis.spatial.VisualObject3D.Companion.DETAIL_KEY
import hep.dataforge.vis.spatial.VisualObject3D.Companion.materialKey import hep.dataforge.vis.spatial.VisualObject3D.Companion.MATERIAL_KEY
import hep.dataforge.vis.spatial.VisualObject3D.Companion.visibleKey import hep.dataforge.vis.spatial.VisualObject3D.Companion.VISIBLE_KEY
data class Value3(var x: Float = 0f, var y: Float = 0f, var z: Float = 0f) data class Value3(var x: Float = 0f, var y: Float = 0f, var z: Float = 0f)
@ -19,13 +18,22 @@ interface VisualObject3D : VisualObject {
var rotation: Value3 var rotation: Value3
var scale: Value3 var scale: Value3
fun setProperty(name: Name, value: Any?) fun MetaBuilder.updatePosition() {
fun getProperty(name: Name, inherit: Boolean = true): MetaItem<*>? xPos to position.x
yPos to position.y
zPos to position.z
xRotation to rotation.x
yRotation to rotation.y
zRotation to rotation.z
xScale to scale.x
yScale to scale.y
zScale to scale.z
}
companion object { companion object {
val materialKey = "material".asName() val MATERIAL_KEY = "material".asName()
val visibleKey = "visible".asName() val VISIBLE_KEY = "visible".asName()
val detailKey = "detail".asName() val DETAIL_KEY = "detail".asName()
val x = "x".asName() val x = "x".asName()
val y = "y".asName() val y = "y".asName()
@ -53,64 +61,32 @@ interface VisualObject3D : VisualObject {
} }
} }
open class VisualLeaf3D(parent: VisualObject?, tagRefs: Array<out Meta>) : VisualLeaf(parent, tagRefs), VisualObject3D { abstract class VisualLeaf3D(parent: VisualObject?) : AbstractVisualObject(parent), VisualObject3D, Configurable {
override var position: Value3 = Value3() override var position: Value3 = Value3()
override var rotation: Value3 = Value3() override var rotation: Value3 = Value3()
override var scale: Value3 = Value3(1f, 1f, 1f) override var scale: Value3 = Value3(1f, 1f, 1f)
private var _config: Config? = null
override val config: Config get() = _config ?: Config().also { _config = it }
override fun setProperty(name: Name, value: Any?) {
config[name] = value
}
override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? {
return if (inherit) {
config[name] ?: (parent as? VisualObject3D)?.getProperty(name, inherit) ?: parent?.properties[name]
} else {
_config?.get(name)
}
}
} }
class VisualGroup3D( class VisualGroup3D(parent: VisualObject? = null) : VisualGroup<VisualObject3D>(parent), VisualObject3D, Configurable {
parent: VisualObject? = null,
tagRefs: Array<out Meta> = emptyArray()
) : VisualGroup(parent, tagRefs), VisualObject3D {
override var position: Value3 = Value3() override var position: Value3 = Value3()
override var rotation: Value3 = Value3() override var rotation: Value3 = Value3()
override var scale: Value3 = Value3(1f, 1f, 1f) override var scale: Value3 = Value3(1f, 1f, 1f)
private var _config: Config? = null override fun MetaBuilder.updateMeta() {
override val config: Config get() = _config ?: Config().also { _config = it } updatePosition()
updateChildren()
override fun setProperty(name: Name, value: Any?) {
config[name] = value
}
override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? {
return if (inherit) {
config[name] ?: (parent as? VisualObject3D)?.getProperty(name, inherit) ?: parent?.properties[name]
} else {
_config?.get(name)
}
} }
} }
fun VisualGroup3D.group(key: String? = null, action: VisualGroup3D.() -> Unit = {}): VisualGroup3D =
VisualGroup3D(this).apply(action).also { set(key, it) }
fun VisualGroup.group(key: String? = null, vararg meta: Meta, action: VisualGroup3D.() -> Unit = {}): VisualGroup3D = fun Output<VisualObject3D>.render(meta: Meta = EmptyMeta, action: VisualGroup3D.() -> Unit) =
VisualGroup3D(this, meta).apply(action).also { set(key, it) }
fun Output<VisualObject>.render(meta: Meta = EmptyMeta, action: VisualGroup3D.() -> Unit) =
render(VisualGroup3D().apply(action), meta) render(VisualGroup3D().apply(action), meta)
// Common properties // Common properties
enum class RotationOrder { enum class RotationOrder {
XYZ, XYZ,
YZX, YZX,
@ -132,16 +108,16 @@ var VisualObject3D.rotationOrder: RotationOrder
* Preferred number of polygons for displaying the object. If not defined, uses shape or renderer default * Preferred number of polygons for displaying the object. If not defined, uses shape or renderer default
*/ */
var VisualObject3D.detail: Int? var VisualObject3D.detail: Int?
get() = getProperty(detailKey).int get() = getProperty(DETAIL_KEY).int
set(value) = setProperty(detailKey, value) set(value) = setProperty(DETAIL_KEY, value)
var VisualObject3D.material: Meta? var VisualObject3D.material: Meta?
get() = getProperty(materialKey).node get() = getProperty(MATERIAL_KEY).node
set(value) = setProperty(materialKey, value) set(value) = setProperty(MATERIAL_KEY, value)
var VisualObject3D.visible: Boolean? var VisualObject3D.visible: Boolean?
get() = getProperty(visibleKey).boolean get() = getProperty(VISIBLE_KEY).boolean
set(value) = setProperty(visibleKey, value) set(value) = setProperty(VISIBLE_KEY, value)
fun VisualObject3D.color(rgb: Int) { fun VisualObject3D.color(rgb: Int) {
material = buildMeta { "color" to rgb } material = buildMeta { "color" to rgb }
@ -157,11 +133,66 @@ fun VisualObject3D.color(r: Int, g: Int, b: Int) = material {
"blue" to b "blue" to b
} }
object World { var VisualObject3D.x: Number
const val CAMERA_INITIAL_DISTANCE = -500.0 get() = position.x
const val CAMERA_INITIAL_X_ANGLE = -50.0 set(value) {
const val CAMERA_INITIAL_Y_ANGLE = 0.0 position.x = value.toFloat()
const val CAMERA_INITIAL_Z_ANGLE = -210.0 propertyChanged(VisualObject3D.xPos)
const val CAMERA_NEAR_CLIP = 0.1 }
const val CAMERA_FAR_CLIP = 10000.0
} var VisualObject3D.y: Number
get() = position.y
set(value) {
position.y = value.toFloat()
propertyChanged(VisualObject3D.yPos)
}
var VisualObject3D.z: Number
get() = position.z
set(value) {
position.z = value.toFloat()
propertyChanged(VisualObject3D.zPos)
}
var VisualObject3D.rotationX: Number
get() = rotation.x
set(value) {
rotation.x = value.toFloat()
propertyChanged(VisualObject3D.xRotation)
}
var VisualObject3D.rotationY: Number
get() = rotation.y
set(value) {
rotation.y = value.toFloat()
propertyChanged(VisualObject3D.xRotation)
}
var VisualObject3D.rotationZ: Number
get() = rotation.z
set(value) {
rotation.z = value.toFloat()
propertyChanged(VisualObject3D.zRotation)
}
var VisualObject3D.scaleX: Number
get() = scale.x
set(value) {
scale.x = value.toFloat()
propertyChanged(VisualObject3D.xScale)
}
var VisualObject3D.scaleY: Number
get() = scale.y
set(value) {
scale.y = value.toFloat()
propertyChanged(VisualObject3D.yScale)
}
var VisualObject3D.scaleZ: Number
get() = scale.z
set(value) {
scale.z = value.toFloat()
propertyChanged(VisualObject3D.zScale)
}

View File

@ -0,0 +1,10 @@
package hep.dataforge.vis.spatial
object World {
const val CAMERA_INITIAL_DISTANCE = -500.0
const val CAMERA_INITIAL_X_ANGLE = -50.0
const val CAMERA_INITIAL_Y_ANGLE = 0.0
const val CAMERA_INITIAL_Z_ANGLE = -210.0
const val CAMERA_NEAR_CLIP = 0.1
const val CAMERA_FAR_CLIP = 10000.0
}

View File

@ -1,37 +0,0 @@
package hep.dataforge.vis.spatial
var VisualObject3D.x: Number
get() = position.x
set(value) {position.x = value.toFloat()}
var VisualObject3D.y: Number
get() = position.y
set(value) {position.y = value.toFloat()}
var VisualObject3D.z: Number
get() = position.z
set(value) {position.z = value.toFloat()}
var VisualObject3D.rotationX: Number
get() = rotation.x
set(value) {rotation.x = value.toFloat()}
var VisualObject3D.rotationY: Number
get() = rotation.y
set(value) {rotation.y = value.toFloat()}
var VisualObject3D.rotationZ: Number
get() = rotation.z
set(value) {rotation.z = value.toFloat()}
var VisualObject3D.scaleX: Number
get() = scale.x
set(value) {scale.x = value.toFloat()}
var VisualObject3D.scaleY: Number
get() = scale.y
set(value) {scale.y = value.toFloat()}
var VisualObject3D.scaleZ: Number
get() = scale.z
set(value) {scale.z = value.toFloat()}

View File

@ -4,14 +4,13 @@ import hep.dataforge.meta.get
import hep.dataforge.meta.getAll import hep.dataforge.meta.getAll
import hep.dataforge.meta.node import hep.dataforge.meta.node
import hep.dataforge.names.toName import hep.dataforge.names.toName
import hep.dataforge.vis.common.VisualGroup
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
class ConvexTest { class ConvexTest {
@Test @Test
fun testConvexBuilder() { fun testConvexBuilder() {
val group = VisualGroup().apply { val group = VisualNode().apply {
convex { convex {
point(50, 50, -50) point(50, 50, -50)
point(50, -50, -50) point(50, -50, -50)

View File

@ -1,7 +1,6 @@
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.vis.common.Colors import hep.dataforge.vis.common.Colors
import hep.dataforge.vis.common.VisualGroup
import hep.dataforge.vis.common.color import hep.dataforge.vis.common.color
import kotlin.math.PI import kotlin.math.PI
import kotlin.test.Test import kotlin.test.Test
@ -10,7 +9,7 @@ import kotlin.test.assertEquals
class GroupTest { class GroupTest {
@Test @Test
fun testGroupWithComposite(){ fun testGroupWithComposite(){
val group = VisualGroup().apply{ val group = VisualNode().apply{
union { union {
box(100, 100, 100) { box(100, 100, 100) {
z = 100 z = 100