GDML demo renders something

This commit is contained in:
Alexander Nozik 2019-07-28 22:00:11 +03:00
parent d3500c3a57
commit e31ad5ece1
12 changed files with 268 additions and 100 deletions

View File

@ -0,0 +1,76 @@
package hep.dataforge.vis.spatial.gdml
import scientifik.gdml.GDMLPosition
import scientifik.gdml.GDMLRotation
import scientifik.gdml.GDMLSolid
import kotlin.math.PI
enum class LUnit(val value: Double) {
MM(1.0),
CM(10.0),
M(1000.0)
}
enum class AUnit(val value: Double) {
DEG(PI / 180),
RAD(1.0),
RADIAN(1.0)
}
fun GDMLPosition.unit(): LUnit = LUnit.valueOf(unit.toUpperCase())
fun GDMLPosition.x(unit: LUnit): Double = if (unit.name == this.unit) {
x.toDouble()
} else {
x.toDouble() / unit.value * unit().value
}
fun GDMLPosition.y(unit: LUnit): Double = if (unit.name == this.unit) {
y.toDouble()
} else {
y.toDouble() / unit.value * unit().value
}
fun GDMLPosition.z(unit: LUnit): Double = if (unit.name == this.unit) {
z.toDouble()
} else {
z.toDouble() / unit.value * unit().value
}
fun GDMLRotation.unit(): AUnit = AUnit.valueOf(unit.toUpperCase())
fun GDMLRotation.x(unit: AUnit = AUnit.RAD): Double = if (unit.name == this.unit) {
x.toDouble()
} else {
x.toDouble() / unit.value * unit().value
}
fun GDMLRotation.y(unit: AUnit = AUnit.RAD): Double = if (unit.name == this.unit) {
y.toDouble()
} else {
y.toDouble() / unit.value * unit().value
}
fun GDMLRotation.z(unit: AUnit = AUnit.RAD): Double = if (unit.name == this.unit) {
z.toDouble()
} else {
z.toDouble() / unit.value * unit().value
}
fun GDMLSolid.lscale(unit: LUnit): Double {
val solidUnit = lunit?.let { LUnit.valueOf(it.toUpperCase()) } ?: return 1.0
return if (solidUnit == unit) {
1.0
} else {
solidUnit.value / unit.value
}
}
fun GDMLSolid.ascale(unit: AUnit = AUnit.RAD): Double {
val solidUnit = aunit?.let { AUnit.valueOf(it.toUpperCase()) } ?: return 1.0
return if (solidUnit == unit) {
1.0
} else {
solidUnit.value / unit.value
}
}

View File

@ -1,7 +1,7 @@
package hep.dataforge.vis.spatial.gdml package hep.dataforge.vis.spatial.gdml
import hep.dataforge.meta.EmptyMeta
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.meta.buildMeta
import hep.dataforge.vis.common.VisualGroup import hep.dataforge.vis.common.VisualGroup
import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.common.VisualObject
import hep.dataforge.vis.common.color import hep.dataforge.vis.common.color
@ -9,66 +9,64 @@ 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
private fun VisualObject.withPosition( private fun VisualObject.withPosition(
lUnit: LUnit,
pos: GDMLPosition? = null, pos: GDMLPosition? = null,
rotation: GDMLRotation? = null, rotation: GDMLRotation? = null,
scale: GDMLScale? = null scale: GDMLScale? = null
): VisualObject = ): VisualObject = apply {
apply {
// if( this is VisualObject3D){
// pos?.let {
// x = pos.x
// y = pos.y
// z = pos.z
// }
// rotation?.let {
// rotationX = rotation.x
// rotationY = rotation.y
// rotationZ = rotation.z
// }
// } else {
pos?.let { pos?.let {
x = pos.x x = pos.x(lUnit)
y = pos.y y = pos.y(lUnit)
z = pos.z z = pos.z(lUnit)
} }
rotation?.let { rotation?.let {
rotationX = rotation.x rotationX = rotation.x()
rotationY = rotation.y rotationY = rotation.y()
rotationZ = rotation.z rotationZ = rotation.z()
} }
//}
scale?.let { scale?.let {
scaleX = scale.x scaleX = scale.x
scaleY = scale.y scaleY = scale.y
scaleZ = scale.z scaleZ = scale.z
} }
//TODO convert units if needed //TODO convert units if needed
} }
private inline operator fun Number.times(d: Double) = toDouble() * d
private fun VisualGroup.addSolid( private fun VisualGroup.addSolid(
root: GDML, root: GDML,
solid: GDMLSolid, solid: GDMLSolid,
lUnit: LUnit,
name: String? = null, name: String? = null,
block: VisualObject.() -> Unit = {} block: VisualObject.() -> Unit = {}
): VisualObject { ): VisualObject {
val lScale = solid.lscale(lUnit)
val aScale = solid.ascale()
return when (solid) { return when (solid) {
is GDMLBox -> box(solid.x, solid.y, solid.z, name) is GDMLBox -> box(solid.x * lScale, solid.y * lScale, solid.z * lScale, name)
is GDMLTube -> cylinder(solid.rmax, solid.z, name) { is GDMLTube -> cylinder(solid.rmax * lScale, solid.z * lScale, name) {
startAngle = solid.startphi startAngle = solid.startphi * aScale
angle = solid.deltaphi angle = solid.deltaphi * aScale
} }
is GDMLXtru -> extrude(name) { is GDMLXtru -> extrude(name) {
shape { shape {
solid.vertices.forEach { solid.vertices.forEach {
point(it.x, it.y) point(it.x * lScale, it.y * lScale)
} }
} }
solid.sections.sortedBy { it.zOrder }.forEach { section -> solid.sections.sortedBy { it.zOrder }.forEach { section ->
layer(section.zPosition, section.xOffset, section.yOffset, section.scalingFactor) layer(
section.zPosition * lScale,
section.xOffset * lScale,
section.yOffset * lScale,
section.scalingFactor
)
} }
} }
is GDMLScaledSolid -> { is GDMLScaledSolid -> {
@ -76,31 +74,31 @@ private fun VisualGroup.addSolid(
val innerSolid = solid.solidref.resolve(root) val innerSolid = solid.solidref.resolve(root)
?: error("Solid with tag ${solid.solidref.ref} for scaled solid ${solid.name} not defined") ?: error("Solid with tag ${solid.solidref.ref} for scaled solid ${solid.name} not defined")
addSolid(root, innerSolid) { addSolid(root, innerSolid, lUnit) {
block() block()
scaleX = scaleX.toDouble() * solid.scale.x.toDouble() scaleX = scaleX.toDouble() * solid.scale.x.toDouble()
scaleY = scaleY.toDouble() * solid.scale.y.toDouble() scaleY = scaleY.toDouble() * solid.scale.y.toDouble()
scaleZ = scaleZ.toDouble() * solid.scale.z.toDouble() scaleZ = scaleZ.toDouble() * solid.scale.z.toDouble()
} }
} }
is GDMLSphere -> sphere(solid.rmax, solid.deltaphi, solid.deltatheta, name) { is GDMLSphere -> sphere(solid.rmax * lScale, solid.deltaphi * aScale, solid.deltatheta * aScale, name) {
phiStart = solid.startphi.toDouble() phiStart = solid.startphi * aScale
thetaStart = solid.starttheta.toDouble() thetaStart = solid.starttheta * aScale
} }
is GDMLOrb -> sphere(solid.r, name = name) is GDMLOrb -> sphere(solid.r * lScale, name = name)
is GDMLPolyhedra -> extrude(name) { is GDMLPolyhedra -> extrude(name) {
//getting the radius of first //getting the radius of first
require(solid.planes.size > 1) { "The polyhedron geometry requires at least two planes" } require(solid.planes.size > 1) { "The polyhedron geometry requires at least two planes" }
val baseRadius = solid.planes.first().rmax.toDouble() val baseRadius = solid.planes.first().rmax * lScale
shape { shape {
(0..solid.numsides).forEach { (0..solid.numsides).forEach {
val phi = solid.deltaphi.toDouble() / solid.numsides * it + solid.startphi.toDouble() val phi = solid.deltaphi * aScale / solid.numsides * it + solid.startphi * aScale
baseRadius * cos(phi) to baseRadius * sin(phi) (baseRadius * cos(phi) to baseRadius * sin(phi))
} }
} }
solid.planes.forEach { plane -> solid.planes.forEach { plane ->
//scaling all radii relative to first layer radius //scaling all radii relative to first layer radius
layer(plane.z, scale = plane.rmax.toDouble() / baseRadius) layer(plane.z * lScale, scale = plane.rmax * lScale / baseRadius)
} }
} }
is GDMLBoolSolid -> { is GDMLBoolSolid -> {
@ -113,19 +111,59 @@ private fun VisualGroup.addSolid(
} }
return composite(type, name) { return composite(type, name) {
addSolid(root, first) { addSolid(root, first, lUnit) {
withPosition(solid.resolveFirstPosition(root), solid.resolveFirstRotation(root), null) withPosition(lUnit, solid.resolveFirstPosition(root), solid.resolveFirstRotation(root), null)
}
addSolid(root, second, lUnit) {
withPosition(lUnit, solid.resolvePosition(root), solid.resolveRotation(root), null)
} }
addSolid(root, second)
withPosition(solid.resolvePosition(root), solid.resolveRotation(root), null)
} }
} }
}.apply(block) }.apply(block)
} }
private fun VisualGroup.addPhysicalVolume(
root: GDML,
physVolume: GDMLPhysVolume,
lUnit: LUnit,
resolveColor: GDMLMaterial.() -> Meta
) {
val volume: GDMLGroup = physVolume.volumeref.resolve(root)
?: error("Volume with ref ${physVolume.volumeref.ref} could not be resolved")
addVolume(
root,
volume,
lUnit,
physVolume.resolvePosition(root),
physVolume.resolveRotation(root),
physVolume.resolveScale(root),
resolveColor
)
}
private fun VisualGroup.addDivisionVolume(
root: GDML,
divisionVolume: GDMLDivisionVolume,
lUnit: LUnit,
resolveColor: GDMLMaterial.() -> Meta
) {
val volume: GDMLGroup = divisionVolume.volumeref.resolve(root)
?: error("Volume with ref ${divisionVolume.volumeref.ref} could not be resolved")
//TODO add divisions
addVolume(
root,
volume,
lUnit,
resolveColor = resolveColor
)
}
private fun VisualGroup.addVolume( private fun VisualGroup.addVolume(
root: GDML, root: GDML,
group: GDMLGroup, group: GDMLGroup,
lUnit: LUnit,
position: GDMLPosition? = null, position: GDMLPosition? = null,
rotation: GDMLRotation? = null, rotation: GDMLRotation? = null,
scale: GDMLScale? = null, scale: GDMLScale? = null,
@ -133,40 +171,37 @@ private fun VisualGroup.addVolume(
) { ) {
group(group.name) { group(group.name) {
withPosition(position, rotation, scale) withPosition(lUnit, position, rotation, scale)
if (group is GDMLVolume) { if (group is GDMLVolume) {
val solid = group.solidref.resolve(root) val solid = group.solidref.resolve(root)
?: error("Solid with tag ${group.solidref.ref} for volume ${group.name} not defined") ?: error("Solid with tag ${group.solidref.ref} for volume ${group.name} not defined")
val material = group.materialref.resolve(root) val material = group.materialref.resolve(root) ?: GDMLElement(group.materialref.ref)
?: error("Material with tag ${group.materialref.ref} for volume ${group.name} not defined") //?: error("Material with tag ${group.materialref.ref} for volume ${group.name} not defined")
addSolid(root, solid, solid.name) { addSolid(root, solid, lUnit, solid.name) {
color(material.resolveColor()) color(material.resolveColor())
} }
//TODO render placements
when (val vol = group.placement) {
is GDMLPhysVolume -> addPhysicalVolume(root, vol, lUnit, resolveColor)
is GDMLDivisionVolume -> addDivisionVolume(root, vol, lUnit, resolveColor)
}
} }
group.physVolumes.forEach { physVolume -> group.physVolumes.forEach { physVolume ->
val volume: GDMLGroup = physVolume.volumeref.resolve(root) addPhysicalVolume(root, physVolume, lUnit, resolveColor)
?: error("Volume with ref ${physVolume.volumeref.ref} could not be resolved")
addVolume(
root,
volume,
physVolume.resolvePosition(root),
physVolume.resolveRotation(root),
physVolume.resolveScale(root),
resolveColor
)
} }
} }
} }
fun GDML.toVisual(lUnit: LUnit = LUnit.MM): VisualGroup {
val cache = HashMap<GDMLMaterial, Meta>()
val random = Random(111)
fun GDML.toVisual(): VisualGroup { fun GDMLMaterial.color(): Meta = cache.getOrPut(this) {
//TODO add materials cache buildMeta { "color" to random.nextInt(0, Int.MAX_VALUE) }
fun GDMLMaterial.color(): Meta = EmptyMeta }
return VisualGroup().also { it.addVolume(this, world) { color() } }
return VisualGroup().also { it.addVolume(this, world, lUnit) { color() } }
} }

View File

@ -3,9 +3,11 @@ package hep.dataforge.vis.spatial.gdml.demo
import hep.dataforge.context.Global import hep.dataforge.context.Global
import hep.dataforge.vis.hmr.ApplicationBase import hep.dataforge.vis.hmr.ApplicationBase
import hep.dataforge.vis.hmr.startApplication import hep.dataforge.vis.hmr.startApplication
import hep.dataforge.vis.spatial.gdml.LUnit
import hep.dataforge.vis.spatial.gdml.toVisual import hep.dataforge.vis.spatial.gdml.toVisual
import hep.dataforge.vis.spatial.three.ThreePlugin import hep.dataforge.vis.spatial.three.ThreePlugin
import hep.dataforge.vis.spatial.three.output import hep.dataforge.vis.spatial.three.output
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.w3c.dom.HTMLDivElement import org.w3c.dom.HTMLDivElement
@ -32,7 +34,7 @@ private class GDMLDemoApp : ApplicationBase() {
/** /**
* Load data from text file * Load data from text file
*/ */
private fun loadData(event: Event, block: suspend (String) -> Unit) { private fun loadData(event: Event, block: suspend CoroutineScope.(String) -> Unit) {
event.stopPropagation() event.stopPropagation()
event.preventDefault() event.preventDefault()
@ -51,6 +53,25 @@ private class GDMLDemoApp : ApplicationBase() {
} }
} }
private fun spinner(show: Boolean) {
val style = if (show) {
"display:block;"
} else {
"display:none;"
}
document.getElementById("loader")?.setAttribute("style", style)
}
private fun message(message: String?) {
val element = document.getElementById("message")
if (message == null) {
element?.setAttribute("style", "display:none;")
} else {
element?.textContent = message
element?.setAttribute("style", "display:block;")
}
}
override fun start(state: Map<String, Any>) { override fun start(state: Map<String, Any>) {
@ -59,13 +80,22 @@ private class GDMLDemoApp : ApplicationBase() {
//val url = URL("https://drive.google.com/open?id=1w5e7fILMN83JGgB8WANJUYm8OW2s0WVO") //val url = URL("https://drive.google.com/open?id=1w5e7fILMN83JGgB8WANJUYm8OW2s0WVO")
val canvas = document.getElementById("canvas") ?: error("Element with id canvas not found on page") val canvas = document.getElementById("canvas") ?: error("Element with id canvas not found on page")
val action: suspend (String) -> Unit = {
canvas.clear() canvas.clear()
val output = three.output(canvas)
val action: suspend CoroutineScope.(String) -> Unit = {
canvas.clear()
launch { spinner(true) }
launch { message("Loading GDML") }
val gdml = GDML.format.parse(GDML.serializer(), it) val gdml = GDML.format.parse(GDML.serializer(), it)
val visual = gdml.toVisual() launch { message("Converting GDML into DF-VIS format") }
val visual = gdml.toVisual(LUnit.CM)
launch { message("Rendering") }
val output = three.output(canvas)
output.render(visual) output.render(visual)
launch {
message(null)
spinner(false)
}
} }
(document.getElementById("drop_zone") as? HTMLDivElement)?.apply { (document.getElementById("drop_zone") as? HTMLDivElement)?.apply {

View File

@ -6,6 +6,7 @@
<title>Three js demo for particle physics</title> <title>Three js demo for particle physics</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"> integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<link rel="stylesheet" href="main.css">
<script type="text/javascript" src="main.bundle.js"></script> <script type="text/javascript" src="main.bundle.js"></script>
</head> </head>
<body class="testApp"> <body class="testApp">
@ -18,6 +19,8 @@
<div class="container"> <div class="container">
<h1>GDML demo</h1> <h1>GDML demo</h1>
</div> </div>
<div class="container loader" id="loader" style="display:none;"></div>
<div class="container animate-bottom" id="message" style="display:none;"></div>
<div class="container" id="canvas"></div> <div class="container" id="canvas"></div>
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"

View File

@ -0,0 +1,13 @@
.loader {
border: 16px solid #f3f3f3; /* Light grey */
border-top: 16px solid #3498db; /* Blue */
border-radius: 50%;
width: 120px;
height: 120px;
animation: spin 2s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}

View File

@ -4,6 +4,7 @@ import hep.dataforge.meta.*
import hep.dataforge.values.ValueType import hep.dataforge.values.ValueType
import hep.dataforge.vis.common.Colors import hep.dataforge.vis.common.Colors
import info.laht.threekt.materials.Material import info.laht.threekt.materials.Material
import info.laht.threekt.materials.MeshBasicMaterial
import info.laht.threekt.materials.MeshPhongMaterial import info.laht.threekt.materials.MeshPhongMaterial
import info.laht.threekt.math.Color import info.laht.threekt.math.Color
@ -39,19 +40,20 @@ fun MetaItem<*>.color(): Color {
} }
/** /**
* Infer FX material based on meta item * Infer Three material based on meta item
*/ */
fun MetaItem<*>?.material(): Material { fun MetaItem<*>?.material(): Material {
return when (this) { return when (this) {
null -> Materials.DEFAULT null -> Materials.DEFAULT
is MetaItem.ValueItem -> MeshPhongMaterial().apply { is MetaItem.ValueItem -> MeshBasicMaterial().apply {
color = this@material.color() color = this@material.color()
} }
is MetaItem.NodeItem -> MeshPhongMaterial().apply { is MetaItem.NodeItem -> MeshBasicMaterial().apply {
(node["color"] ?: this@material).let { color = it.color() } (node["color"] ?: this@material).let { color = it.color() }
opacity = node["opacity"]?.double ?: 1.0 opacity = node["opacity"]?.double ?: 1.0
transparent = node["transparent"].boolean ?: (opacity < 1.0) transparent = node["transparent"].boolean ?: (opacity < 1.0)
node["specularColor"]?.let { specular = it.color() } //node["specularColor"]?.let { specular = it.color() }
side = 2
} }
} }
} }

View File

@ -4,15 +4,19 @@ import hep.dataforge.meta.Meta
import hep.dataforge.meta.get import hep.dataforge.meta.get
import hep.dataforge.meta.int import hep.dataforge.meta.int
import hep.dataforge.vis.spatial.GeometryBuilder import hep.dataforge.vis.spatial.GeometryBuilder
import hep.dataforge.vis.spatial.Point2D
import hep.dataforge.vis.spatial.Point3D import hep.dataforge.vis.spatial.Point3D
import info.laht.threekt.core.BufferGeometry import info.laht.threekt.core.BufferGeometry
import info.laht.threekt.core.Face3 import info.laht.threekt.core.Face3
import info.laht.threekt.core.Geometry import info.laht.threekt.core.Geometry
import info.laht.threekt.math.Vector2
import info.laht.threekt.math.Vector3 import info.laht.threekt.math.Vector3
// TODO use unsafe cast instead // TODO use unsafe cast instead
fun Point3D.asVector(): Vector3 = Vector3(this.x, this.y, this.z) fun Point3D.asVector(): Vector3 = Vector3(this.x, this.y, this.z)
fun Point2D.asVector(): Vector2 = Vector2(this.x, this.y)
class ThreeGeometryBuilder : GeometryBuilder<BufferGeometry> { class ThreeGeometryBuilder : GeometryBuilder<BufferGeometry> {
private val vertices = ArrayList<Point3D>() private val vertices = ArrayList<Point3D>()
@ -38,6 +42,7 @@ class ThreeGeometryBuilder : GeometryBuilder<BufferGeometry> {
faces.add(face) faces.add(face)
} }
override fun build(): BufferGeometry { override fun build(): BufferGeometry {
return Geometry().apply { return Geometry().apply {
vertices = this@ThreeGeometryBuilder.vertices.map { it.asVector() }.toTypedArray() vertices = this@ThreeGeometryBuilder.vertices.map { it.asVector() }.toTypedArray()

View File

@ -41,8 +41,9 @@ class ThreePlugin : AbstractPlugin() {
is VisualGroup -> Group(obj.mapNotNull { is VisualGroup -> Group(obj.mapNotNull {
try { try {
buildObject3D(it) buildObject3D(it)
} catch (ex: Throwable){ } catch (ex: Throwable) {
logger.error(ex){"Failed to render $it"} console.error(ex)
logger.error(ex) { "Failed to render $it" }
null null
} }
}).apply { }).apply {

View File

@ -2,10 +2,11 @@ package hep.dataforge.vis.spatial
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
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
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, meta: Array<out Meta>) :
VisualObject3D(parent, meta), Shape { VisualLeaf(parent, meta), Shape {
//TODO add helper for color configuration //TODO add helper for color configuration

View File

@ -4,6 +4,7 @@ import hep.dataforge.meta.Meta
import hep.dataforge.meta.seal import hep.dataforge.meta.seal
import hep.dataforge.meta.update import hep.dataforge.meta.update
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
enum class CompositeType { enum class CompositeType {
@ -18,7 +19,7 @@ open class Composite(
val second: VisualObject, val second: VisualObject,
val type: CompositeType = CompositeType.UNION, val type: CompositeType = CompositeType.UNION,
meta: Array<out Meta> meta: Array<out Meta>
) : VisualObject3D(parent, meta) ) : VisualLeaf(parent, meta)
fun VisualGroup.composite( fun VisualGroup.composite(
type: CompositeType, type: CompositeType,

View File

@ -45,10 +45,23 @@ class Extruded(parent: VisualObject?, meta: Array<out Meta>) : VisualLeaf(parent
val layers: MutableList<Layer> = ArrayList() val layers: MutableList<Layer> = ArrayList()
fun layer(z: Number, x: Number = 0.0, y: Number = 0.0, scale: Number = 1.0) { fun layer(z: Number, x: Number = 0.0, y: Number = 0.0, scale: Number = 1.0) {
layers.add(Layer(x,y,z,scale)) layers.add(Layer(x, y, z, scale))
//TODO send invalidation signal //TODO send invalidation signal
} }
private fun <T : Any> GeometryBuilder<T>.cap(shape: List<Point3D>) {
//FIXME won't work for non-convex shapes
val center = Point3D(
shape.map { it.x.toDouble() }.average(),
shape.map { it.y.toDouble() }.average(),
shape.map { it.z.toDouble() }.average()
)
for(i in 0 until (shape.size - 1)){
face(shape[i], shape[i+1], center, null)
}
face(shape.last(), shape.first(),center,null)
}
override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) { override fun <T : Any> toGeometry(geometryBuilder: GeometryBuilder<T>) {
val shape: Shape2D = shape val shape: Shape2D = shape
@ -91,6 +104,8 @@ class Extruded(parent: VisualObject?, meta: Array<out Meta>) : VisualLeaf(parent
) )
lowerLayer = upperLayer lowerLayer = upperLayer
} }
geometryBuilder.cap(layers.first().reversed())
geometryBuilder.cap(layers.last())
} }
companion object { companion object {

View File

@ -4,23 +4,9 @@ import hep.dataforge.meta.*
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.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
/**
* Performance optimized version of visual object
*/
open class VisualObject3D(parent: VisualObject?, tagRefs: Array<out Meta>) : VisualLeaf(parent, tagRefs) {
var x: Number? = null; get() = field ?: (this as VisualLeaf).x
var y: Number? = null; get() = field ?: (this as VisualLeaf).y
var z: Number? = null; get() = field ?: (this as VisualLeaf).z
var rotationX: Number? = null; get() = field ?: (this as VisualLeaf).rotationX
var rotationY: Number? = null; get() = field ?: (this as VisualLeaf).rotationY
var rotationZ: Number? = null; get() = field ?: (this as VisualLeaf).rotationZ
}
fun VisualGroup.group(key: String? = null, vararg meta: Meta, action: VisualGroup.() -> Unit = {}): VisualGroup = fun VisualGroup.group(key: String? = null, vararg meta: Meta, action: VisualGroup.() -> Unit = {}): VisualGroup =
VisualGroup(this, meta).apply(action).also { set(key, it) } VisualGroup(this, meta).apply(action).also { set(key, it) }