GDML optimization

This commit is contained in:
Alexander Nozik 2020-08-26 21:12:39 +03:00
parent a5eba1789b
commit b362f86f9b
12 changed files with 179 additions and 223 deletions

View File

@ -20,7 +20,7 @@ fun cubes(): GDML = GDML {
val circle = volume("composite", boxMaterial, smallBox.ref()) {
for (i in 0 until 6) {
physVolume(segmentVolume) {
name = "segment$i"
name = "segment_$i"
positionref = center.ref()
rotation {
z = 60 * i

View File

@ -10,11 +10,21 @@ import kotlin.test.Test
import kotlin.test.assertEquals
class GDMLVisualTest {
val gdml = cubes()
// @Test
// fun testCubesStyles(){
// val cubes = gdml.toVision()
// val segment = cubes["composite000.segment_0".toName()] as Solid
// println(segment.styles)
// println(segment.material)
// }
@Test
fun testPrototypeProperty() {
val gdml = cubes()
val visual = gdml.toVision()
visual["composite000.segment0".toName()]?.setItem(SolidMaterial.MATERIAL_COLOR_KEY, "red".asValue())
assertEquals("red", visual["composite000.segment0".toName()]?.getItem(SolidMaterial.MATERIAL_COLOR_KEY).string)
visual["composite000.segment_0".toName()]?.setItem(SolidMaterial.MATERIAL_COLOR_KEY, "red".asValue())
assertEquals("red", visual["composite000.segment_0".toName()]?.getItem(SolidMaterial.MATERIAL_COLOR_KEY).string)
}
}

View File

@ -84,7 +84,6 @@ class StyleSheet private constructor(private val styleMap: MutableMap<String, Me
override fun serialize(encoder: Encoder, value: StyleSheet) {
mapSerializer.serialize(encoder, value.items)
}
}
}
@ -93,7 +92,7 @@ class StyleSheet private constructor(private val styleMap: MutableMap<String, Me
* Add style name to the list of styles to be resolved later. The style with given name does not necessary exist at the moment.
*/
fun Vision.useStyle(name: String) {
styles = styles + name
styles = properties[Vision.STYLE_KEY].stringList + name
}
/**

View File

@ -5,7 +5,6 @@ import hep.dataforge.names.Name
import hep.dataforge.names.asName
import hep.dataforge.names.toName
import hep.dataforge.provider.Type
import hep.dataforge.values.asValue
import hep.dataforge.vision.Vision.Companion.TYPE
import kotlinx.serialization.PolymorphicSerializer
import kotlinx.serialization.Transient
@ -63,7 +62,7 @@ interface Vision : Configurable {
var styles: List<String>
get() = properties[STYLE_KEY].stringList
set(value) {
setItem(STYLE_KEY,value.map { it.asValue() }.asValue())
config[STYLE_KEY] = value
}
companion object {

View File

@ -6,7 +6,6 @@ import hep.dataforge.names.Name
import hep.dataforge.names.asName
import hep.dataforge.names.plus
import hep.dataforge.names.toName
import hep.dataforge.vision.get
import hep.dataforge.vision.set
import hep.dataforge.vision.solid.*
import hep.dataforge.vision.solid.SolidMaterial.Companion.MATERIAL_COLOR_KEY
@ -27,6 +26,7 @@ class GDMLTransformer(val root: GDML) {
}
var lUnit: LUnit = LUnit.MM
var aUnit: AUnit = AUnit.RADIAN
var solidAction: (GDMLSolid) -> Action = { Action.CACHE }
var volumeAction: (GDMLGroup) -> Action = { Action.CACHE }
@ -65,10 +65,6 @@ class GDMLTransformer(val root: GDML) {
obj.solidConfiguration(parent, solid)
}
//
// internal fun solidAdded(solid: GDMLSolid) {
// solidCounter[solid.name] = (solidCounter[solid.name] ?: 0) + 1
// }
var onFinish: GDMLTransformer.() -> Unit = {}
@ -94,6 +90,7 @@ class GDMLTransformer(val root: GDML) {
private fun Solid.withPosition(
lUnit: LUnit,
aUnit: AUnit = AUnit.RADIAN,
newPos: GDMLPosition? = null,
newRotation: GDMLRotation? = null,
newScale: GDMLScale? = null
@ -105,7 +102,7 @@ private fun Solid.withPosition(
}
}
newRotation?.let {
val point = Point3D(it.x(), it.y(), it.z())
val point = Point3D(it.x(aUnit), it.y(aUnit), it.z(aUnit))
if (rotation != null || point != World.ZERO) {
rotation = point
}
@ -120,6 +117,13 @@ private fun Solid.withPosition(
//TODO convert units if needed
}
private fun Solid.withPosition(context: GDMLTransformer, physVolume: GDMLPhysVolume) = withPosition(
context.lUnit, context.aUnit,
physVolume.resolvePosition(context.root),
physVolume.resolveRotation(context.root),
physVolume.resolveScale(context.root)
)
@Suppress("NOTHING_TO_INLINE")
private inline operator fun Number.times(d: Double) = toDouble() * d
@ -129,8 +133,7 @@ private inline operator fun Number.times(f: Float) = toFloat() * f
private fun SolidGroup.addSolid(
context: GDMLTransformer,
solid: GDMLSolid,
name: String = "",
block: Solid.() -> Unit = {}
name: String = ""
): Solid {
//context.solidAdded(solid)
val lScale = solid.lscale(context.lUnit)
@ -168,11 +171,10 @@ private fun SolidGroup.addSolid(
}
is GDMLScaledSolid -> {
//Add solid with modified scale
val innerSolid = solid.solidref.resolve(context.root)
val innerSolid: GDMLSolid = solid.solidref.resolve(context.root)
?: error("Solid with tag ${solid.solidref.ref} for scaled solid ${solid.name} not defined")
addSolid(context, innerSolid, name) {
block()
addSolid(context, innerSolid, name).apply {
scaleX *= solid.scale.x.toFloat()
scaleY *= solid.scale.y.toFloat()
scaleZ = solid.scale.z.toFloat()
@ -199,8 +201,8 @@ private fun SolidGroup.addSolid(
}
}
is GDMLBoolSolid -> {
val first = solid.first.resolve(context.root) ?: error("")
val second = solid.second.resolve(context.root) ?: error("")
val first: GDMLSolid = solid.first.resolve(context.root) ?: error("")
val second: GDMLSolid = solid.second.resolve(context.root) ?: error("")
val type: CompositeType = when (solid) {
is GDMLUnion -> CompositeType.UNION
is GDMLSubtraction -> CompositeType.SUBTRACT
@ -208,54 +210,51 @@ private fun SolidGroup.addSolid(
}
return composite(type, name) {
addSolid(context, first) {
withPosition(
context.lUnit,
solid.resolveFirstPosition(context.root),
solid.resolveFirstRotation(context.root),
null
)
}
addSolid(context, second) {
withPosition(
context.lUnit,
solid.resolvePosition(context.root),
solid.resolveRotation(context.root),
null
)
}
addSolid(context, first).withPosition(
context.lUnit, context.aUnit,
solid.resolveFirstPosition(context.root),
solid.resolveFirstRotation(context.root),
null
)
addSolid(context, second).withPosition(
context.lUnit, context.aUnit,
solid.resolvePosition(context.root),
solid.resolveRotation(context.root),
null
)
}
}
else -> error("Renderer for $solid not supported yet")
}.apply(block)
}
}
private val solidsName = "solids".asName()
private fun SolidGroup.addSolidWithCaching(
context: GDMLTransformer,
solid: GDMLSolid,
volume: GDMLVolume,
name: String = solid.name
) {
when (context.solidAction(solid)) {
): Solid? {
return when (context.solidAction(solid)) {
GDMLTransformer.Action.ACCEPT -> {
addSolid(context, solid, name) {
context.configureSolid(this, volume, solid)
}
addSolid(context, solid, name)
}
GDMLTransformer.Action.CACHE -> {
if (context.proto[solid.name] == null) {
context.proto.addSolid(context, solid, name) {
context.configureSolid(this, volume, solid)
}
val fullName = solidsName + solid.name.asName()
if (context.proto[fullName] == null) {
val parent = (context.proto[solidsName] as? SolidGroup) ?: context.proto.group(solidsName)
parent.addSolid(context, solid, solid.name)
}
ref(solid.name.asName(), name)
ref(fullName, name)
}
GDMLTransformer.Action.REJECT -> {
//ignore
null
}
}
}
private val volumesName = "volumes".asName()
@ -268,31 +267,20 @@ private fun SolidGroup.addPhysicalVolume(
?: error("Volume with ref ${physVolume.volumeref.ref} could not be resolved")
// a special case for single solid volume
// if (volume is GDMLVolume && volume.physVolumes.isEmpty() && volume.placement == null) {
// val solid = volume.solidref.resolve(context.root)
// ?: error("Solid with tag ${volume.solidref.ref} for volume ${volume.name} not defined")
// addSolidWithCaching(context, solid, volume, physVolume.name ?: "").apply {
// withPosition(
// context.lUnit,
// physVolume.resolvePosition(context.root),
// physVolume.resolveRotation(context.root),
// physVolume.resolveScale(context.root)
// )
// }
// return
// }
if (volume is GDMLVolume && volume.physVolumes.isEmpty() && volume.placement == null) {
val solid = volume.solidref.resolve(context.root)
?: error("Solid with tag ${volume.solidref.ref} for volume ${volume.name} not defined")
addSolidWithCaching(context, solid, physVolume.name ?: "")?.apply {
context.configureSolid(this, volume, solid)
withPosition(context, physVolume)
}
return
}
when (context.volumeAction(volume)) {
GDMLTransformer.Action.ACCEPT -> {
val group: SolidGroup = volume(context, volume)
this[physVolume.name ?: ""] = group.apply {
withPosition(
context.lUnit,
physVolume.resolvePosition(context.root),
physVolume.resolveRotation(context.root),
physVolume.resolveScale(context.root)
)
}
this[physVolume.name ?: ""] = group.withPosition(context, physVolume)
}
GDMLTransformer.Action.CACHE -> {
val fullName = volumesName + volume.name.asName()
@ -300,14 +288,7 @@ private fun SolidGroup.addPhysicalVolume(
context.proto[fullName] = volume(context, volume)
}
this[physVolume.name ?: ""] = Proxy(this, fullName).apply {
withPosition(
context.lUnit,
physVolume.resolvePosition(context.root),
physVolume.resolveRotation(context.root),
physVolume.resolveScale(context.root)
)
}
this[physVolume.name ?: ""] = Proxy(this, fullName).withPosition(context, physVolume)
}
GDMLTransformer.Action.REJECT -> {
//ignore
@ -326,8 +307,6 @@ private fun SolidGroup.addDivisionVolume(
set(Name.EMPTY, volume(context, volume))
}
//private val solidsName = "solids".asName()
private fun volume(
context: GDMLTransformer,
group: GDMLGroup
@ -336,7 +315,9 @@ private fun volume(
val solid: GDMLSolid = group.solidref.resolve(context.root)
?: error("Solid with tag ${group.solidref.ref} for volume ${group.name} not defined")
addSolidWithCaching(context, solid, group)
addSolidWithCaching(context, solid)?.apply {
context.configureSolid(this, group, solid)
}
when (val vol: GDMLPlacement? = group.placement) {
is GDMLPhysVolume -> addPhysicalVolume(context, vol)

View File

@ -5,7 +5,6 @@ package hep.dataforge.vision.solid
import hep.dataforge.meta.*
import hep.dataforge.meta.descriptors.NodeDescriptor
import hep.dataforge.names.*
import hep.dataforge.values.asValue
import hep.dataforge.vision.*
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@ -13,36 +12,8 @@ import kotlinx.serialization.Transient
import kotlinx.serialization.UseSerializers
import kotlin.collections.set
class AbstractProxy
/**
* A proxy [Solid] to reuse a template object
*/
@Serializable
@SerialName("solid.proxy")
class Proxy private constructor(
val templateName: Name
) : AbstractVision(), VisionGroup, Solid {
constructor(parent: SolidGroup, templateName: Name) : this(templateName) {
this.parent = parent
}
override var position: Point3D? = null
override var rotation: Point3D? = null
override var scale: Point3D? = null
override var properties: Config? = null
/**
* Recursively search for defined template in the parent
*/
val prototype: Solid
get() = (parent as? SolidGroup)?.getPrototype(templateName)
?: error("Prototype with name $templateName not found in $parent")
override val styleSheet: StyleSheet get() = parent?.styleSheet ?: StyleSheet(this)
abstract class AbstractProxy : AbstractVision(), VisionGroup {
abstract val prototype: Vision
override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? {
return if (inherit) {
@ -61,24 +32,11 @@ class Proxy private constructor(
}
}
override val children: Map<NameToken, ProxyChild>
get() = (prototype as? VisionGroup)?.children
?.filter { !it.key.toString().startsWith("@") }
?.mapValues {
ProxyChild(it.key.asName())
} ?: emptyMap()
@Transient
private val propertyCache: HashMap<Name, Config> = HashMap()
fun childPropertyName(childName: Name, propertyName: Name): Name {
return NameToken(PROXY_CHILD_PROPERTY_PREFIX, childName.toString()) + propertyName
}
private fun prototypeFor(name: Name): Vision {
return (prototype as? VisionGroup)?.get(name)
?: error("Prototype with name $name not found in $this")
}
override var styles: List<String>
get() = properties[Vision.STYLE_KEY].stringList + prototype.styles
set(value) {
config[Vision.STYLE_KEY] = value
}
override fun getAllProperties(): Laminate =
Laminate(properties, allStyles, prototype.getAllProperties(), parent?.getAllProperties())
@ -87,29 +45,77 @@ class Proxy private constructor(
//do nothing
}
override val descriptor: NodeDescriptor? get() = prototype.descriptor
}
/**
* A proxy [Solid] to reuse a template object
*/
@Serializable
@SerialName("solid.proxy")
class Proxy private constructor(
val templateName: Name
) : AbstractProxy(), Solid {
constructor(parent: SolidGroup, templateName: Name) : this(templateName) {
this.parent = parent
}
override var position: Point3D? = null
override var rotation: Point3D? = null
override var scale: Point3D? = null
override var properties: Config? = null
/**
* Recursively search for defined template in the parent
*/
override val prototype: Solid
get() = (parent as? SolidGroup)?.getPrototype(templateName)
?: error("Prototype with name $templateName not found in $parent")
override val styleSheet: StyleSheet get() = parent?.styleSheet ?: StyleSheet(this)
@Transient
private val propertyCache: HashMap<Name, Config> = HashMap()
override val children: Map<NameToken, Proxy.ProxyChild>
get() = (prototype as? VisionGroup)?.children
?.filter { !it.key.toString().startsWith("@") }
?.mapValues {
ProxyChild(it.key.asName())
} ?: emptyMap()
private fun childPropertyName(childName: Name, propertyName: Name): Name {
return NameToken(PROXY_CHILD_PROPERTY_PREFIX, childName.toString()) + propertyName
}
private fun prototypeFor(name: Name): Vision {
return (prototype as? VisionGroup)?.get(name)
?: error("Prototype with name $name not found in $this")
}
override val descriptor: NodeDescriptor? get() = prototype.descriptor
//override fun findAllStyles(): Laminate = Laminate((styles + prototype.styles).mapNotNull { findStyle(it) })
override var styles: List<String>
get() = properties[Vision.STYLE_KEY].stringList + prototype.styles
set(value) {
setItem(Vision.STYLE_KEY, value.map { it.asValue() }.asValue())
}
/**
* A ProxyChild is created temporarily only to interact with properties, it does not store any values
* (properties are stored in external cache) and created and destroyed on-demand).
*/
inner class ProxyChild(val name: Name) : AbstractProxy() {
override val descriptor: NodeDescriptor?
get() = prototype.descriptor
inner class ProxyChild(val name: Name) : AbstractVision(), VisionGroup {
val prototype: Vision get() = prototypeFor(name)
override val prototype: Vision get() = prototypeFor(name)
override val styleSheet: StyleSheet get() = this@Proxy.styleSheet
override val children: Map<NameToken, Vision>
get() = (prototype as? VisionGroup)?.children?.mapValues { (key, _) ->
ProxyChild(
name + key.asName()
)
} ?: emptyMap()
get() = (prototype as? VisionGroup)?.children
?.filter { !it.key.toString().startsWith("@") }
?.mapValues { (key, _) ->
ProxyChild(name + key.asName())
} ?: emptyMap()
override var properties: Config?
get() = propertyCache[name]
@ -128,39 +134,6 @@ class Proxy private constructor(
}
}
override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? {
return if (inherit) {
sequence {
yield(properties?.get(name))
yieldAll(getStyleItems(name))
yield(prototype.getItem(name))
yield(parent?.getProperty(name, inherit))
}.merge()
} else {
sequence {
yield(properties?.get(name))
yieldAll(getStyleItems(name))
yield(prototype.getProperty(name, false))
}.merge()
}
}
override fun attachChildren() {
//do nothing
}
override fun getAllProperties(): Laminate =
Laminate(properties, allStyles, prototype.getAllProperties(), parent?.getAllProperties())
override var styles: List<String>
get() = properties[Vision.STYLE_KEY].stringList + prototype.styles
set(value) {
setItem(Vision.STYLE_KEY, value.map { it.asValue() }.asValue())
}
override val descriptor: NodeDescriptor?
get() = prototype.descriptor
}
companion object {
@ -170,8 +143,7 @@ class Proxy private constructor(
val Vision.prototype: Vision
get() = when (this) {
is Proxy -> prototype
is Proxy.ProxyChild -> prototype
is AbstractProxy -> prototype
else -> this
}

View File

@ -99,13 +99,14 @@ fun SolidGroup(block: SolidGroup.() -> Unit): SolidGroup {
tailrec fun PrototypeHolder.getPrototype(name: Name): Solid? =
prototypes?.get(name) as? Solid ?: (parent as? PrototypeHolder)?.getPrototype(name)
fun MutableVisionGroup.group(name: Name = Name.EMPTY, action: SolidGroup.() -> Unit = {}): SolidGroup =
SolidGroup().apply(action).also { set(name, it) }
/**
* Define a group with given [name], attach it to this parent and return it.
*/
fun MutableVisionGroup.group(name: String = "", action: SolidGroup.() -> Unit = {}): SolidGroup =
SolidGroup().apply(action).also {
set(name, it)
}
fun MutableVisionGroup.group(name: String, action: SolidGroup.() -> Unit = {}): SolidGroup =
SolidGroup().apply(action).also { set(name, it) }
/**
* A special class which works as a holder for prototypes

View File

@ -35,19 +35,11 @@ abstract class MeshThreeFactory<in T : Solid>(
//val meshMeta: Meta = obj.properties[Material3D.MATERIAL_KEY]?.node ?: Meta.empty
val mesh = Mesh(geometry, getMaterial(obj, true)).apply {
val mesh = Mesh(geometry, null).apply{
matrixAutoUpdate = false
applyEdges(obj)
applyWireFrame(obj)
//set position for mesh
updatePosition(obj)
layers.enable(obj.layer)
children.forEach {
it.layers.enable(obj.layer)
}
}
}.applyProperties(obj)
//add listener to object properties
obj.onPropertyChange(this) { name ->
@ -79,6 +71,16 @@ abstract class MeshThreeFactory<in T : Solid>(
}
}
fun Mesh.applyProperties(obj: Solid): Mesh = apply{
material = getMaterial(obj, true)
applyEdges(obj)
applyWireFrame(obj)
layers.enable(obj.layer)
children.forEach {
it.layers.enable(obj.layer)
}
}
fun Mesh.applyEdges(obj: Solid) {
val edges = children.find { it.name == "@edges" } as? LineSegments
//inherited edges definition, enabled by default

View File

@ -41,18 +41,6 @@ fun Object3D.updatePosition(obj: Vision) {
}
}
///**
// * Unsafe invocation of a factory
// */
//operator fun <T : VisualObject3D> ThreeFactory<T>.invoke(obj: Any): Object3D {
// if (type.isInstance(obj)) {
// @Suppress("UNCHECKED_CAST")
// return invoke(obj as T)
// } else {
// error("The object of type ${obj::class} could not be rendered by this factory")
// }
//}
/**
* Update non-position non-geometry property
*/

View File

@ -4,7 +4,6 @@ import hep.dataforge.names.toName
import hep.dataforge.vision.solid.Proxy
import hep.dataforge.vision.solid.Proxy.Companion.PROXY_CHILD_PROPERTY_PREFIX
import hep.dataforge.vision.solid.Solid
import info.laht.threekt.core.BufferGeometry
import info.laht.threekt.core.Object3D
import info.laht.threekt.objects.Mesh
import kotlin.reflect.KClass
@ -14,16 +13,16 @@ class ThreeProxyFactory(val three: ThreePlugin) : ThreeFactory<Proxy> {
override val type: KClass<Proxy> = Proxy::class
private fun Object3D.replicate(): Object3D {
return when (this) {
is Mesh -> Mesh(geometry as BufferGeometry, material)
else -> clone(false)
}.also { obj: Object3D ->
children.forEach { child: Object3D ->
obj.add(child.replicate())
}
}
}
// private fun Object3D.replicate(): Object3D {
// return when (this) {
// is Mesh -> Mesh(geometry as BufferGeometry, material)
// else -> clone(false)
// }.also { obj: Object3D ->
// children.forEach { child: Object3D ->
// obj.add(child.replicate())
// }
// }
// }
override fun invoke(obj: Proxy): Object3D {
val template = obj.prototype
@ -31,10 +30,13 @@ class ThreeProxyFactory(val three: ThreePlugin) : ThreeFactory<Proxy> {
three.buildObject3D(template)
}
val object3D: Object3D = cachedObject.replicate()
val object3D: Object3D = cachedObject.clone()//cachedObject.replicate()
object3D.updatePosition(obj)
if(object3D is Mesh){
object3D.applyProperties(obj)
}
obj.onPropertyChange(this) { name ->
if (name.first()?.body == PROXY_CHILD_PROPERTY_PREFIX) {
val childName = name.first()?.index?.toName() ?: error("Wrong syntax for proxy child property: '$name'")

View File

@ -1,6 +1,7 @@
package hep.dataforge.vision.solid.three
import hep.dataforge.js.requireJS
import hep.dataforge.meta.DFExperimental
import hep.dataforge.vision.bootstrap.accordion
import hep.dataforge.vision.bootstrap.entry
import hep.dataforge.vision.solid.SolidGroup
@ -31,6 +32,7 @@ private fun saveData(event: Event, fileName: String, mimeType: String = "text/pl
fileSaver.saveAs(blob, fileName)
}
@OptIn(DFExperimental::class)
fun RBuilder.canvasControls(canvas: ThreeCanvas) = accordion("controls") {
entry("Settings") {
div("row") {

View File

@ -33,8 +33,8 @@ import info.laht.threekt.materials.Material
open external class Mesh : Object3D {
constructor(geometry: Geometry, material: Material)
constructor(geometry: BufferGeometry, material: Material)
constructor(geometry: Geometry?, material: Material?)
constructor(geometry: BufferGeometry?, material: Material?)
var geometry: dynamic
var material: Material