Fixed after migration to new Group structure
This commit is contained in:
parent
589afaa9b8
commit
8f1dcac6cd
@ -54,10 +54,24 @@ abstract class AbstractVisualGroup : AbstractVisualObject(), VisualGroup {
|
|||||||
//
|
//
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a child with given name token
|
||||||
|
*/
|
||||||
protected abstract fun removeChild(token: NameToken)
|
protected abstract fun removeChild(token: NameToken)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add, remove or replace child with given name
|
||||||
|
*/
|
||||||
protected abstract fun setChild(token: NameToken, child: VisualObject?)
|
protected abstract fun setChild(token: NameToken, child: VisualObject?)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a static child. Statics could not be found by name, removed or replaced
|
||||||
|
*/
|
||||||
|
protected abstract fun addStatic(child: VisualObject)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively create a child group
|
||||||
|
*/
|
||||||
protected abstract fun createGroup(name: Name): VisualGroup
|
protected abstract fun createGroup(name: Name): VisualGroup
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -66,17 +80,16 @@ abstract class AbstractVisualGroup : AbstractVisualObject(), VisualGroup {
|
|||||||
*/
|
*/
|
||||||
override fun set(name: Name, child: VisualObject?) {
|
override fun set(name: Name, child: VisualObject?) {
|
||||||
when {
|
when {
|
||||||
name.isEmpty() -> error("")
|
name.isEmpty() -> {
|
||||||
|
if (child != null) {
|
||||||
|
addStatic(child)
|
||||||
|
}
|
||||||
|
}
|
||||||
name.length == 1 -> {
|
name.length == 1 -> {
|
||||||
val token = name.first()!!
|
val token = name.first()!!
|
||||||
if (child == null) {
|
if (child == null) {
|
||||||
removeChild(token)
|
removeChild(token)
|
||||||
} else {
|
} else {
|
||||||
if (child.parent == null) {
|
|
||||||
child.parent = this
|
|
||||||
} else {
|
|
||||||
error("Can't reassign existing parent for $child")
|
|
||||||
}
|
|
||||||
setChild(token, child)
|
setChild(token, child)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -89,7 +102,13 @@ abstract class AbstractVisualGroup : AbstractVisualObject(), VisualGroup {
|
|||||||
listeners.forEach { it.callback(name, child) }
|
listeners.forEach { it.callback(name, child) }
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun set(key: String, child: VisualObject?) = set(key.asName(), child)
|
operator fun set(key: String, child: VisualObject?) = if (key.isBlank()) {
|
||||||
|
child?.let { addStatic(child) }
|
||||||
|
} else {
|
||||||
|
set(key.asName(), child)
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun set(key: String?, child: VisualObject?) = set(key ?: "", child)
|
||||||
|
|
||||||
protected fun MetaBuilder.updateChildren() {
|
protected fun MetaBuilder.updateChildren() {
|
||||||
//adding named children
|
//adding named children
|
||||||
|
@ -34,8 +34,7 @@ abstract class AbstractVisualObject : VisualObject {
|
|||||||
abstract var properties: Config?
|
abstract var properties: Config?
|
||||||
override val config: Config
|
override val config: Config
|
||||||
get() = properties ?: Config().also { config ->
|
get() = properties ?: Config().also { config ->
|
||||||
properties = config
|
properties = config.apply { onChange(this, ::propertyChanged) }
|
||||||
config.onChange(this, ::propertyChanged)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setProperty(name: Name, value: Any?) {
|
override fun setProperty(name: Name, value: Any?) {
|
||||||
|
@ -50,4 +50,4 @@ interface VisualGroup : VisualObject, Provider, Iterable<VisualObject> {
|
|||||||
operator fun set(name: Name, child: VisualObject?)
|
operator fun set(name: Name, child: VisualObject?)
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun VisualGroup.get(str: String) = get(str.toName())
|
operator fun VisualGroup.get(str: String?) = get(str?.toName() ?: EmptyName)
|
@ -1,8 +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.names.EmptyName
|
||||||
import hep.dataforge.names.plus
|
import hep.dataforge.names.plus
|
||||||
import hep.dataforge.vis.common.asName
|
import hep.dataforge.vis.common.asName
|
||||||
|
import hep.dataforge.vis.common.get
|
||||||
import hep.dataforge.vis.spatial.*
|
import hep.dataforge.vis.spatial.*
|
||||||
import scientifik.gdml.*
|
import scientifik.gdml.*
|
||||||
import kotlin.math.cos
|
import kotlin.math.cos
|
||||||
@ -42,7 +44,7 @@ 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,
|
||||||
name: String? = null,
|
name: String = "",
|
||||||
block: VisualObject3D.() -> Unit = {}
|
block: VisualObject3D.() -> Unit = {}
|
||||||
): VisualObject3D {
|
): VisualObject3D {
|
||||||
context.solidAdded(solid)
|
context.solidAdded(solid)
|
||||||
@ -185,7 +187,8 @@ private fun VisualGroup3D.addDivisionVolume(
|
|||||||
?: error("Volume with ref ${divisionVolume.volumeref.ref} could not be resolved")
|
?: error("Volume with ref ${divisionVolume.volumeref.ref} could not be resolved")
|
||||||
|
|
||||||
//TODO add divisions
|
//TODO add divisions
|
||||||
add(
|
set(
|
||||||
|
EmptyName,
|
||||||
volume(
|
volume(
|
||||||
context,
|
context,
|
||||||
volume
|
volume
|
||||||
@ -217,7 +220,7 @@ private fun volume(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
GDMLTransformer.Action.CACHE -> {
|
GDMLTransformer.Action.CACHE -> {
|
||||||
if (context.templates[solid.name] == null) {
|
if (context.templates.get(solid.name) == null) {
|
||||||
context.templates.addSolid(context, solid, solid.name) {
|
context.templates.addSolid(context, solid, solid.name) {
|
||||||
this.material = context.resolveColor(group, material, solid)
|
this.material = context.resolveColor(group, material, solid)
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ import kotlinx.serialization.UseSerializers
|
|||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Box(
|
class Box(
|
||||||
val xSize: Float,
|
val xSize: Float,
|
||||||
val ySize: Float,
|
val ySize: Float,
|
||||||
val zSize: Float
|
val zSize: Float
|
||||||
|
@ -3,12 +3,16 @@ package hep.dataforge.vis.spatial
|
|||||||
import hep.dataforge.context.AbstractPlugin
|
import hep.dataforge.context.AbstractPlugin
|
||||||
import hep.dataforge.context.PluginFactory
|
import hep.dataforge.context.PluginFactory
|
||||||
import hep.dataforge.context.PluginTag
|
import hep.dataforge.context.PluginTag
|
||||||
|
import hep.dataforge.io.ConfigSerializer
|
||||||
|
import hep.dataforge.io.MetaSerializer
|
||||||
|
import hep.dataforge.io.NameSerializer
|
||||||
import hep.dataforge.meta.*
|
import hep.dataforge.meta.*
|
||||||
import hep.dataforge.names.Name
|
import hep.dataforge.names.Name
|
||||||
import hep.dataforge.vis.common.VisualPlugin
|
import hep.dataforge.vis.common.VisualPlugin
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import kotlinx.serialization.json.JsonConfiguration
|
import kotlinx.serialization.json.JsonConfiguration
|
||||||
import kotlinx.serialization.modules.SerializersModule
|
import kotlinx.serialization.modules.SerializersModule
|
||||||
|
import kotlinx.serialization.modules.contextual
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
class Visual3DPlugin(meta: Meta) : AbstractPlugin(meta) {
|
class Visual3DPlugin(meta: Meta) : AbstractPlugin(meta) {
|
||||||
@ -28,6 +32,12 @@ class Visual3DPlugin(meta: Meta) : AbstractPlugin(meta) {
|
|||||||
override fun invoke(meta: Meta): Visual3DPlugin = Visual3DPlugin(meta)
|
override fun invoke(meta: Meta): Visual3DPlugin = Visual3DPlugin(meta)
|
||||||
|
|
||||||
val serialModule = SerializersModule {
|
val serialModule = SerializersModule {
|
||||||
|
contextual(Point3DSerializer)
|
||||||
|
contextual(Point2DSerializer)
|
||||||
|
contextual(NameSerializer)
|
||||||
|
contextual(NameTokenSerializer)
|
||||||
|
contextual(MetaSerializer)
|
||||||
|
contextual(ConfigSerializer)
|
||||||
polymorphic(VisualObject3D::class) {
|
polymorphic(VisualObject3D::class) {
|
||||||
VisualGroup3D::class with VisualGroup3D.serializer()
|
VisualGroup3D::class with VisualGroup3D.serializer()
|
||||||
Proxy::class with Proxy.serializer()
|
Proxy::class with Proxy.serializer()
|
||||||
|
@ -0,0 +1,92 @@
|
|||||||
|
@file:UseSerializers(Point3DSerializer::class, ConfigSerializer::class, NameTokenSerializer::class)
|
||||||
|
package hep.dataforge.vis.spatial
|
||||||
|
|
||||||
|
import hep.dataforge.io.ConfigSerializer
|
||||||
|
import hep.dataforge.meta.Config
|
||||||
|
import hep.dataforge.meta.Configurable
|
||||||
|
import hep.dataforge.meta.MetaBuilder
|
||||||
|
import hep.dataforge.meta.set
|
||||||
|
import hep.dataforge.names.Name
|
||||||
|
import hep.dataforge.names.NameToken
|
||||||
|
import hep.dataforge.names.asName
|
||||||
|
import hep.dataforge.names.isEmpty
|
||||||
|
import hep.dataforge.vis.common.AbstractVisualGroup
|
||||||
|
import hep.dataforge.vis.common.VisualObject
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.UseSerializers
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class VisualGroup3D : AbstractVisualGroup(), VisualObject3D, Configurable {
|
||||||
|
/**
|
||||||
|
* A container for templates visible inside this group
|
||||||
|
*/
|
||||||
|
var templates: VisualGroup3D? = null
|
||||||
|
set(value) {
|
||||||
|
value?.parent = this
|
||||||
|
field = value
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable(ConfigSerializer::class)
|
||||||
|
override var properties: Config? = null
|
||||||
|
|
||||||
|
override var position: Point3D? = null
|
||||||
|
override var rotation: Point3D? = null
|
||||||
|
override var scale: Point3D? = null
|
||||||
|
|
||||||
|
private val _children = HashMap<NameToken, VisualObject>()
|
||||||
|
override val children: Map<NameToken, VisualObject> get() = _children
|
||||||
|
|
||||||
|
override fun removeChild(token: NameToken) {
|
||||||
|
_children.remove(token)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setChild(token: NameToken, child: VisualObject?) {
|
||||||
|
if (child == null) {
|
||||||
|
_children.remove(token)
|
||||||
|
} else {
|
||||||
|
if (child.parent == null) {
|
||||||
|
child.parent = this
|
||||||
|
} else {
|
||||||
|
error("Can't reassign existing parent for $child")
|
||||||
|
}
|
||||||
|
_children[token] = child
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO add special static group to hold statics without propagation
|
||||||
|
*/
|
||||||
|
override fun addStatic(child: VisualObject) = setChild(NameToken(child.hashCode().toString()), child)
|
||||||
|
|
||||||
|
override fun createGroup(name: Name): VisualGroup3D {
|
||||||
|
return when {
|
||||||
|
name.isEmpty() -> error("Should be unreachable")
|
||||||
|
name.length == 1 -> {
|
||||||
|
val token = name.first()!!
|
||||||
|
when (val current = children[token]) {
|
||||||
|
null -> VisualGroup3D().also { setChild(token, it) }
|
||||||
|
is VisualGroup3D -> current
|
||||||
|
else -> error("Can't create group with name $name because it exists and not a group")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> createGroup(name.first()!!.asName()).createGroup(name.cutFirst())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getTemplate(name: Name): VisualObject3D? =
|
||||||
|
templates?.get(name) as? VisualObject3D
|
||||||
|
?: (parent as? VisualGroup3D)?.getTemplate(name)
|
||||||
|
|
||||||
|
override fun MetaBuilder.updateMeta() {
|
||||||
|
set(TEMPLATES_KEY, templates?.toMeta())
|
||||||
|
updatePosition()
|
||||||
|
updateChildren()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val TEMPLATES_KEY = "templates"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun VisualGroup3D.group(key: String = "", action: VisualGroup3D.() -> Unit = {}): VisualGroup3D =
|
||||||
|
VisualGroup3D().apply(action).also { set(key, it) }
|
@ -2,18 +2,15 @@
|
|||||||
|
|
||||||
package hep.dataforge.vis.spatial
|
package hep.dataforge.vis.spatial
|
||||||
|
|
||||||
import hep.dataforge.io.ConfigSerializer
|
|
||||||
import hep.dataforge.io.NameSerializer
|
import hep.dataforge.io.NameSerializer
|
||||||
import hep.dataforge.meta.*
|
import hep.dataforge.meta.*
|
||||||
import hep.dataforge.names.*
|
import hep.dataforge.names.plus
|
||||||
import hep.dataforge.output.Output
|
import hep.dataforge.output.Output
|
||||||
import hep.dataforge.vis.common.AbstractVisualGroup
|
|
||||||
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.DETAIL_KEY
|
import hep.dataforge.vis.spatial.VisualObject3D.Companion.DETAIL_KEY
|
||||||
import hep.dataforge.vis.spatial.VisualObject3D.Companion.MATERIAL_KEY
|
import hep.dataforge.vis.spatial.VisualObject3D.Companion.MATERIAL_KEY
|
||||||
import hep.dataforge.vis.spatial.VisualObject3D.Companion.VISIBLE_KEY
|
import hep.dataforge.vis.spatial.VisualObject3D.Companion.VISIBLE_KEY
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
import kotlinx.serialization.UseSerializers
|
import kotlinx.serialization.UseSerializers
|
||||||
|
|
||||||
interface VisualObject3D : VisualObject {
|
interface VisualObject3D : VisualObject {
|
||||||
@ -64,71 +61,6 @@ interface VisualObject3D : VisualObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
|
||||||
class VisualGroup3D : AbstractVisualGroup(), VisualObject3D, Configurable {
|
|
||||||
/**
|
|
||||||
* A container for templates visible inside this group
|
|
||||||
*/
|
|
||||||
var templates: VisualGroup3D? = null
|
|
||||||
set(value) {
|
|
||||||
value?.parent = this
|
|
||||||
field = value
|
|
||||||
}
|
|
||||||
|
|
||||||
@Serializable(ConfigSerializer::class)
|
|
||||||
override var properties: Config? = null
|
|
||||||
|
|
||||||
override var position: Point3D? = null
|
|
||||||
override var rotation: Point3D? = null
|
|
||||||
override var scale: Point3D? = null
|
|
||||||
|
|
||||||
private val _children = HashMap<NameToken, VisualObject>()
|
|
||||||
override val children: Map<NameToken, VisualObject> get() = _children
|
|
||||||
|
|
||||||
override fun removeChild(token: NameToken) {
|
|
||||||
_children.remove(token)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setChild(token: NameToken, child: VisualObject?) {
|
|
||||||
if (child == null) {
|
|
||||||
_children.remove(token)
|
|
||||||
} else {
|
|
||||||
_children[token] = child
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun createGroup(name: Name): VisualGroup3D {
|
|
||||||
return when{
|
|
||||||
name.isEmpty() -> error("Should be unreachable")
|
|
||||||
name.length == 1 -> {
|
|
||||||
val token = name.first()!!
|
|
||||||
when (val current = children[token]) {
|
|
||||||
null -> VisualGroup3D().also { setChild(token, it) }
|
|
||||||
is VisualGroup3D -> current
|
|
||||||
else -> error("Can't create group with name $name because it exists and not a group")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> createGroup(name.first()!!.asName()).createGroup(name.cutFirst())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getTemplate(name: Name): VisualObject3D? =
|
|
||||||
templates?.get(name) as? VisualGroup3D ?: (parent as? VisualGroup3D)?.getTemplate(name)
|
|
||||||
|
|
||||||
override fun MetaBuilder.updateMeta() {
|
|
||||||
set(TEMPLATES_KEY, templates?.toMeta())
|
|
||||||
updatePosition()
|
|
||||||
updateChildren()
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val TEMPLATES_KEY = "templates"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun VisualGroup3D.group(key: String = "", action: VisualGroup3D.() -> Unit = {}): VisualGroup3D =
|
|
||||||
VisualGroup3D().apply(action).also { set(key, it) }
|
|
||||||
|
|
||||||
fun Output<VisualObject3D>.render(meta: Meta = EmptyMeta, action: VisualGroup3D.() -> Unit) =
|
fun Output<VisualObject3D>.render(meta: Meta = EmptyMeta, action: VisualGroup3D.() -> Unit) =
|
||||||
render(VisualGroup3D().apply(action), meta)
|
render(VisualGroup3D().apply(action), meta)
|
||||||
|
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package hep.dataforge.vis.spatial
|
package hep.dataforge.vis.spatial
|
||||||
|
|
||||||
import hep.dataforge.names.NameToken
|
import hep.dataforge.names.NameToken
|
||||||
|
import hep.dataforge.names.toName
|
||||||
import kotlinx.serialization.*
|
import kotlinx.serialization.*
|
||||||
|
import kotlinx.serialization.internal.StringDescriptor
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
private data class Point2DSerial(val x: Double, val y: Double)
|
private data class Point2DSerial(val x: Double, val y: Double)
|
||||||
@ -42,4 +44,14 @@ object Point2DSerializer : KSerializer<Point2D>{
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Serializer(NameToken::class)
|
@Serializer(NameToken::class)
|
||||||
object NameTokenSerializer : KSerializer<NameToken>
|
object NameTokenSerializer : KSerializer<NameToken> {
|
||||||
|
override val descriptor: SerialDescriptor = StringDescriptor.withName("NameToken")
|
||||||
|
|
||||||
|
override fun deserialize(decoder: Decoder): NameToken {
|
||||||
|
return decoder.decodeString().toName().first()!!
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun serialize(encoder: Encoder, obj: NameToken) {
|
||||||
|
encoder.encodeString(obj.toString())
|
||||||
|
}
|
||||||
|
}
|
@ -56,7 +56,7 @@ fun Meta?.jsMaterial(): Material {
|
|||||||
opacity = get("opacity")?.double ?: 1.0
|
opacity = get("opacity")?.double ?: 1.0
|
||||||
transparent = get("transparent").boolean ?: (opacity < 1.0)
|
transparent = get("transparent").boolean ?: (opacity < 1.0)
|
||||||
//node["specularColor"]?.let { specular = it.color() }
|
//node["specularColor"]?.let { specular = it.color() }
|
||||||
side = 2
|
//side = 2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -90,7 +90,7 @@ abstract class MeshThreeFactory<T : VisualObject3D>(override val type: KClass<ou
|
|||||||
|
|
||||||
override fun invoke(obj: T): Mesh {
|
override fun invoke(obj: T): Mesh {
|
||||||
//create mesh from geometry
|
//create mesh from geometry
|
||||||
return buildMesh<T>(obj) { buildGeometry(it) }
|
return buildMesh(obj) { buildGeometry(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -5,6 +5,9 @@ import hep.dataforge.context.PluginFactory
|
|||||||
import hep.dataforge.context.PluginTag
|
import hep.dataforge.context.PluginTag
|
||||||
import hep.dataforge.context.content
|
import hep.dataforge.context.content
|
||||||
import hep.dataforge.meta.*
|
import hep.dataforge.meta.*
|
||||||
|
import hep.dataforge.names.Name
|
||||||
|
import hep.dataforge.names.asName
|
||||||
|
import hep.dataforge.names.isEmpty
|
||||||
import hep.dataforge.vis.spatial.*
|
import hep.dataforge.vis.spatial.*
|
||||||
import info.laht.threekt.cameras.Camera
|
import info.laht.threekt.cameras.Camera
|
||||||
import info.laht.threekt.cameras.PerspectiveCamera
|
import info.laht.threekt.cameras.PerspectiveCamera
|
||||||
@ -46,7 +49,7 @@ class ThreePlugin : AbstractPlugin() {
|
|||||||
object3D.name = name.toString()
|
object3D.name = name.toString()
|
||||||
group.add(object3D)
|
group.add(object3D)
|
||||||
} catch (ex: Throwable) {
|
} catch (ex: Throwable) {
|
||||||
console.error(ex)
|
// console.error(ex)
|
||||||
logger.error(ex) { "Failed to render $name" }
|
logger.error(ex) { "Failed to render $name" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -98,3 +101,11 @@ class ThreePlugin : AbstractPlugin() {
|
|||||||
override fun invoke(meta: Meta) = ThreePlugin()
|
override fun invoke(meta: Meta) = ThreePlugin()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Object3D.findChild(name: Name): Object3D? {
|
||||||
|
return when {
|
||||||
|
name.isEmpty() -> this
|
||||||
|
name.length == 1 -> this.children.find { it.name == name.first()!!.toString() }
|
||||||
|
else -> findChild(name.first()!!.asName())?.findChild(name.cutFirst())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user